Compare commits
5 Commits
baf891e5ed
...
0748bcf4b8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0748bcf4b8 | ||
![]() |
7377c316db | ||
![]() |
e6616356a2 | ||
![]() |
afabcd057f | ||
![]() |
056b388520 |
38
README.md
38
README.md
@ -1,17 +1,47 @@
|
||||
# Whisky Business
|
||||
|
||||
Refresh the OLCC DB:
|
||||
```plantuml
|
||||
@startuml
|
||||
object location {
|
||||
address
|
||||
phone
|
||||
id
|
||||
}
|
||||
|
||||
```bash
|
||||
node whisky.js
|
||||
object whisky {
|
||||
label
|
||||
price
|
||||
average[52] : statewide average count per week
|
||||
id
|
||||
location[] --> location : count
|
||||
}
|
||||
|
||||
object client
|
||||
object server
|
||||
|
||||
object user {
|
||||
location
|
||||
radius
|
||||
whisky[]
|
||||
}
|
||||
user --> whisky : monitor
|
||||
|
||||
@enduml
|
||||
```
|
||||
|
||||
# Whisky Business
|
||||
|
||||
Launch GraphQL server:
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
Refresh the OLCC DB:
|
||||
|
||||
```bash
|
||||
node whisky.js
|
||||
```
|
||||
|
||||
Launch website:
|
||||
|
||||
```bash
|
||||
|
@ -53,7 +53,9 @@ const WhiskyType = new graphql.GraphQLObjectType({
|
||||
lastSeen: { type: graphql.GraphQLString },
|
||||
size: { type: graphql.GraphQLInt },
|
||||
price: { type: graphql.GraphQLFloat },
|
||||
inventories: { type: graphql.GraphQLList(InventoryType) }
|
||||
inventories: { type: graphql.GraphQLList(InventoryType) },
|
||||
quantity: { type: graphql.GraphQLInt },
|
||||
updated: { type: graphql.GraphQLString }
|
||||
})
|
||||
});
|
||||
|
||||
@ -91,7 +93,9 @@ const buildWhiskies = (rows) => {
|
||||
price: row.price,
|
||||
size: row.size,
|
||||
code: row.code,
|
||||
inventories: []
|
||||
inventories: [],
|
||||
quantity: 0,
|
||||
updated: 0
|
||||
}
|
||||
whiskies[row.code] = whisky;
|
||||
} else {
|
||||
@ -110,6 +114,8 @@ const buildWhiskies = (rows) => {
|
||||
quantity: row.quantity,
|
||||
updated: row.updated
|
||||
});
|
||||
whisky.updated = (new Date(row.updated) > new Date(whisky.updated)) ? row.updated : whisky.updated;
|
||||
whisky.quantity += row.quantity;
|
||||
}
|
||||
});
|
||||
const results = [];
|
||||
@ -176,14 +182,20 @@ var queryType = new graphql.GraphQLObjectType({
|
||||
fields: {
|
||||
Whiskies: {
|
||||
type: graphql.GraphQLList(WhiskyType),
|
||||
resolve: (root, args, context, info) => {
|
||||
args: {
|
||||
code: { type: graphql.GraphQLString },
|
||||
},
|
||||
resolve: (root, {code}, context, info) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (code && code.trim() != "") {
|
||||
code = `${code.replace(/%/g, '')}%`;
|
||||
}
|
||||
database.all(
|
||||
"SELECT w.*,l.code AS 'location',l.address,l.city,l.phone,l.latitude,l.longitude,i.quantity,i.updated " +
|
||||
"SELECT w.*,i.quantity,i.updated " +
|
||||
"FROM Whiskies AS w " +
|
||||
"LEFT JOIN Inventories AS i ON w.code=i.whisky "+
|
||||
"LEFT JOIN Locations AS l ON l.code=i.location " +
|
||||
";", function(err, rows) {
|
||||
"LEFT JOIN Inventories AS i ON w.code=i.whisky " +
|
||||
(code ? "WHERE w.code LIKE (?) OR w.description LIKE (?)" : "") +
|
||||
";", [code, code], function(err, rows) {
|
||||
if (err) { console.error(err); return reject(null); }
|
||||
resolve(buildWhiskies(rows));
|
||||
});
|
||||
@ -198,9 +210,9 @@ var queryType = new graphql.GraphQLObjectType({
|
||||
resolve: (root, {code}, context, info) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
database.all(
|
||||
"SELECT w.*,l.code AS 'location',l.address,l.city,l.phone,l.latitude,l.longitude,i.quantity,i.updated "+
|
||||
"SELECT w.*,l.code AS 'location',l.address,l.city,l.phone,l.latitude,l.longitude,i.quantity,i.updated " +
|
||||
"FROM Whiskies AS w " +
|
||||
"LEFT JOIN Inventories AS i ON w.code=i.whisky "+
|
||||
"LEFT JOIN Inventories AS i ON w.code=i.whisky " +
|
||||
"LEFT JOIN Locations AS l ON l.code=i.location " +
|
||||
"WHERE w.code = (?);", [code], function(err, rows) {
|
||||
if (err) { console.error(err); return reject(null); }
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { StatusBar } from 'expo-status-bar';
|
||||
import React, { useState } from 'react';
|
||||
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
||||
import React, { useState, useReducer } from 'react';
|
||||
import { StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
||||
import { request, GraphQLClient, gql } from 'graphql-request';
|
||||
import moment from 'moment';
|
||||
import { buildExecutionContext } from 'graphql/execution/execute';
|
||||
@ -14,20 +14,22 @@ const Whisky = (props : { active?: boolean, whisky: any, style: any }) => {
|
||||
|
||||
const locations: any[] = [];
|
||||
|
||||
whisky.inventories.forEach((item : { location: any, quantity: number, updated: string }) => {
|
||||
quantity += item.quantity;
|
||||
const updated = moment(item.updated, 'YYYY-MM-DD');
|
||||
if (time < updated) {
|
||||
time = updated;
|
||||
}
|
||||
locations.push(
|
||||
<View key={item.location.code} style={[styles.horizontal,styles.container]}>
|
||||
<Text style={whiskyStyles.address}>{item.location.address}</Text>
|
||||
<Text style={whiskyStyles.phone}>{item.location.phone}</Text>
|
||||
<Text style={whiskyStyles.quantity}>{item.quantity}</Text>
|
||||
</View>
|
||||
)
|
||||
});
|
||||
if (whisky.inventories) {
|
||||
whisky.inventories.forEach((item : { location: any, quantity: number, updated: string }) => {
|
||||
quantity += item.quantity;
|
||||
const updated = moment(item.updated, 'YYYY-MM-DD');
|
||||
if (time < updated) {
|
||||
time = updated;
|
||||
}
|
||||
locations.push(
|
||||
<View key={item.location.code} style={[styles.horizontal,styles.container]}>
|
||||
<Text style={whiskyStyles.address}>{item.location.address}</Text>
|
||||
<Text style={whiskyStyles.phone}>{item.location.phone}</Text>
|
||||
<Text style={whiskyStyles.quantity}>{item.quantity}</Text>
|
||||
</View>
|
||||
)
|
||||
});
|
||||
}
|
||||
const date = (time.unix() == 0) ? "" : time.format("YYYY-MM-DD");
|
||||
|
||||
return (
|
||||
@ -36,7 +38,7 @@ const Whisky = (props : { active?: boolean, whisky: any, style: any }) => {
|
||||
<View style={whiskyStyles.code}><Text>{whisky.code}</Text></View>
|
||||
<View style={whiskyStyles.description}><Text>{whisky.description}</Text></View>
|
||||
<View style={whiskyStyles.date}><Text>{date}</Text></View>
|
||||
<View style={whiskyStyles.quantity}><Text>{quantity}</Text></View>
|
||||
<View style={whiskyStyles.quantity}><Text>{whisky.quantity}</Text></View>
|
||||
</View>
|
||||
{ props.active && <View style={styles.vertical}>
|
||||
{ locations }
|
||||
@ -84,14 +86,63 @@ const whiskyStyles = StyleSheet.create({
|
||||
|
||||
export default function App() {
|
||||
const [whiskies, setWhiskies] = useState<any>(null),
|
||||
[search, setSearch] = useState<string>(""),
|
||||
[lastSearch, setLastSearch] = useState<string>(""),
|
||||
[activeWhisky, setActiveWhisky] = useState<string>("");
|
||||
|
||||
const [, forceUpdate] = useReducer(x => x + 1, 0);
|
||||
|
||||
const updateWhisky = (code : string) => {
|
||||
const query = gql` {
|
||||
Whisky(code: "${code}") {
|
||||
inventories {
|
||||
location {
|
||||
code
|
||||
address
|
||||
city
|
||||
phone
|
||||
longitude
|
||||
latitude
|
||||
}
|
||||
quantity
|
||||
updated
|
||||
}
|
||||
code
|
||||
size
|
||||
description
|
||||
updated
|
||||
quantity
|
||||
}
|
||||
}`;
|
||||
|
||||
client.request(query)
|
||||
.then(data => {
|
||||
for (let i = 0; i < whiskies.length; i++) {
|
||||
if (whiskies[i].code == data.Whisky.code) {
|
||||
whiskies[i] = data.Whisky;
|
||||
break;
|
||||
}
|
||||
}
|
||||
setWhiskies(whiskies);
|
||||
forceUpdate();
|
||||
})
|
||||
.catch (error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const onPress = (code : string) => {
|
||||
setActiveWhisky(code == activeWhisky ? "" : code);
|
||||
if (code == activeWhisky) {
|
||||
setActiveWhisky("");
|
||||
} else {
|
||||
setActiveWhisky(code);
|
||||
updateWhisky(code);
|
||||
}
|
||||
};
|
||||
|
||||
const items = whiskies ? whiskies
|
||||
.filter((item : any) => item.size == 750 && item.inventories.length)
|
||||
.filter((item : any) => item.size == 750)
|
||||
.sort((a : any, b : any) => a.description.localeCompare(b.description))
|
||||
.map((whisky : any) => {
|
||||
const active = whisky.code == activeWhisky;
|
||||
return (
|
||||
@ -104,39 +155,54 @@ export default function App() {
|
||||
);
|
||||
}) : [];
|
||||
|
||||
let query = gql` {
|
||||
Whiskies {
|
||||
code
|
||||
description
|
||||
price
|
||||
size
|
||||
lastSeen
|
||||
inventories {
|
||||
location {
|
||||
code
|
||||
address
|
||||
city
|
||||
phone
|
||||
longitude
|
||||
latitude
|
||||
}
|
||||
const submitSearch = () => {
|
||||
if (search.trim() == "") {
|
||||
setWhiskies([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (lastSearch == search) {
|
||||
return;
|
||||
}
|
||||
|
||||
const _query = gql` {
|
||||
Whiskies(code:"${search}") {
|
||||
code
|
||||
description
|
||||
price
|
||||
size
|
||||
lastSeen
|
||||
quantity
|
||||
updated
|
||||
}
|
||||
}
|
||||
}`;
|
||||
}`;
|
||||
|
||||
setLastSearch(search);
|
||||
|
||||
client.request(query)
|
||||
.then(data => {
|
||||
setWhiskies(data.Whiskies);
|
||||
})
|
||||
.catch (error => {
|
||||
console.error(error);
|
||||
});
|
||||
client.request(_query)
|
||||
.then(data => {
|
||||
setWhiskies(data.Whiskies);
|
||||
})
|
||||
.catch (error => {
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
const keyPress = (event : KeyboardEvent) => {
|
||||
if (event.code == "\n") {
|
||||
submitSearch();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={[styles.container]}>
|
||||
<TextInput style={[{borderWidth: 1 }]}
|
||||
onBlur={submitSearch}
|
||||
onKeyPress={(event : any) => keyPress(event)}
|
||||
onChange={(value) => setSearch(value.target.value)}></TextInput>
|
||||
<View style={[styles.container]}>
|
||||
{ items }
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
11
whisky.js
11
whisky.js
@ -215,7 +215,6 @@ const rateLimit = async () => {
|
||||
}
|
||||
const delay = (lastFetch + (Math.random() * 5000 + 2500)) - Date.now();
|
||||
if (delay > 0) {
|
||||
// console.log(`Rate limiting for ${delay}ms`);
|
||||
await wait(delay);
|
||||
}
|
||||
lastFetch = Date.now();
|
||||
@ -349,7 +348,7 @@ fs.stat("cache")
|
||||
})
|
||||
.then((response) => {
|
||||
if (response.status != 200) {
|
||||
throw "Error fetching resource from oregonliquorsearch";
|
||||
throw new Error("Error fetching resource from oregonliquorsearch");
|
||||
}
|
||||
});
|
||||
}).then(async () => {
|
||||
@ -382,8 +381,7 @@ fs.stat("cache")
|
||||
|
||||
const rows = dom.window.document.querySelectorAll('table.list tr');
|
||||
const olccWhiskies = [];
|
||||
|
||||
rows.forEach(row => {
|
||||
const processRow = (row) => {
|
||||
const th = row.querySelectorAll('table.list th a');
|
||||
if (th.length) {
|
||||
th.forEach(header => headers.push(header.textContent.trim()));
|
||||
@ -407,7 +405,7 @@ fs.stat("cache")
|
||||
case 'Proof':
|
||||
if (parseFloat(value) == value) {
|
||||
value = parseFloat(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'Case Price':
|
||||
@ -433,8 +431,9 @@ fs.stat("cache")
|
||||
whisky[headers[index]] = value;
|
||||
});
|
||||
olccWhiskies.push(whisky);
|
||||
});
|
||||
};
|
||||
|
||||
rows.forEach(processRow);
|
||||
return olccWhiskies;
|
||||
}).then(async (olccWhiskies) => {
|
||||
await Promise.all(olccWhiskies.map(async (olccWhisky) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user