Functions for shares catalog endpoints

merge-requests/30/head
Bob Mottram 2021-07-24 23:08:11 +01:00
parent 723e6e7a5e
commit 5d5dab6d2f
25 changed files with 262 additions and 46 deletions

View File

@ -13631,6 +13631,10 @@ class PubServer(BaseHTTPRequestHandler):
return -1 return -1
if not fields.get('itemType'): if not fields.get('itemType'):
return -1 return -1
if not fields.get('itemPrice'):
return -1
if not fields.get('itemCurrency'):
return -1
if not fields.get('category'): if not fields.get('category'):
return -1 return -1
if not fields.get('location'): if not fields.get('location'):
@ -13652,6 +13656,13 @@ class PubServer(BaseHTTPRequestHandler):
if fields['itemQty']: if fields['itemQty']:
if fields['itemQty'].isdigit(): if fields['itemQty'].isdigit():
itemQty = int(fields['itemQty']) itemQty = int(fields['itemQty'])
itemPrice = "0"
if fields['itemPrice']:
if fields['itemPrice'].isdigit():
itemPrice = fields['itemPrice']
itemCurrency = "EUR"
if fields['itemCurrency']:
itemCurrency = fields['itemCurrency']
addShare(self.server.baseDir, addShare(self.server.baseDir,
self.server.httpPrefix, self.server.httpPrefix,
nickname, nickname,
@ -13664,7 +13675,7 @@ class PubServer(BaseHTTPRequestHandler):
fields['location'], fields['location'],
durationStr, durationStr,
self.server.debug, self.server.debug,
city, city, itemPrice, itemCurrency,
self.server.systemLanguage, self.server.systemLanguage,
self.server.translate) self.server.translate)
if filename: if filename:

View File

@ -561,6 +561,12 @@ parser.add_argument('--itemImage', dest='itemImage', type=str,
parser.add_argument('--itemQty', dest='itemQty', type=int, parser.add_argument('--itemQty', dest='itemQty', type=int,
default=1, default=1,
help='Quantity of items being shared') help='Quantity of items being shared')
parser.add_argument('--itemPrice', dest='itemPrice', type=str,
default="0",
help='Total price of items being shared')
parser.add_argument('--itemCurrency', dest='itemCurrency', type=str,
default="EUR",
help='Currency of items being shared')
parser.add_argument('--itemType', dest='itemType', type=str, parser.add_argument('--itemType', dest='itemType', type=str,
default=None, default=None,
help='Type of item being shared') help='Type of item being shared')
@ -1290,7 +1296,8 @@ if args.itemName:
args.location, args.location,
args.duration, args.duration,
cachedWebfingers, personCache, cachedWebfingers, personCache,
debug, __version__) debug, __version__,
args.itemPrice, args.itemCurrency)
for i in range(10): for i in range(10):
# TODO detect send success/fail # TODO detect send success/fail
time.sleep(1) time.sleep(1)
@ -2330,7 +2337,7 @@ if args.testdata:
"img/shares1.png", "img/shares1.png",
1, "tool", 1, "tool",
"mechanical", "mechanical",
"City", "City", "0", "GBP",
"2 months", "2 months",
debug, city, args.language, {}) debug, city, args.language, {})
addShare(baseDir, addShare(baseDir,
@ -2340,7 +2347,7 @@ if args.testdata:
"img/shares2.png", "img/shares2.png",
1, "hat", 1, "hat",
"clothing", "clothing",
"City", "City", "0", "GBP",
"3 months", "3 months",
debug, city, args.language, {}) debug, city, args.language, {})

165
shares.py
View File

@ -9,6 +9,7 @@ __module_group__ = "Timeline"
import os import os
import time import time
import datetime
from webfinger import webfingerHandle from webfinger import webfingerHandle
from auth import createBasicAuthHeader from auth import createBasicAuthHeader
from posts import getPersonBox from posts import getPersonBox
@ -180,6 +181,7 @@ def addShare(baseDir: str,
displayName: str, summary: str, imageFilename: str, displayName: str, summary: str, imageFilename: str,
itemQty: int, itemType: str, itemCategory: str, location: str, itemQty: int, itemType: str, itemCategory: str, location: str,
duration: str, debug: bool, city: str, duration: str, debug: bool, city: str,
price: str, currency: str,
systemLanguage: str, translate: {}) -> None: systemLanguage: str, translate: {}) -> None:
"""Adds a new share """Adds a new share
""" """
@ -241,7 +243,9 @@ def addShare(baseDir: str,
"category": itemCategory, "category": itemCategory,
"location": location, "location": location,
"published": published, "published": published,
"expire": durationSec "expire": durationSec,
"price": "0",
"currency": ""
} }
saveJson(sharesJson, sharesFilename) saveJson(sharesJson, sharesFilename)
@ -283,25 +287,27 @@ def _expireSharesForAccount(baseDir: str, nickname: str, domain: str) -> None:
handleDomain = removeDomainPort(domain) handleDomain = removeDomainPort(domain)
handle = nickname + '@' + handleDomain handle = nickname + '@' + handleDomain
sharesFilename = baseDir + '/accounts/' + handle + '/shares.json' sharesFilename = baseDir + '/accounts/' + handle + '/shares.json'
if os.path.isfile(sharesFilename): if not os.path.isfile(sharesFilename):
sharesJson = loadJson(sharesFilename) return
if sharesJson: sharesJson = loadJson(sharesFilename)
currTime = int(time.time()) if not sharesJson:
deleteItemID = [] return
for itemID, item in sharesJson.items(): currTime = int(time.time())
if currTime > item['expire']: deleteItemID = []
deleteItemID.append(itemID) for itemID, item in sharesJson.items():
if deleteItemID: if currTime > item['expire']:
for itemID in deleteItemID: deleteItemID.append(itemID)
del sharesJson[itemID] if not deleteItemID:
# remove any associated images return
itemIDfile = \ for itemID in deleteItemID:
baseDir + '/sharefiles/' + nickname + '/' + itemID del sharesJson[itemID]
formats = getImageExtensions() # remove any associated images
for ext in formats: itemIDfile = baseDir + '/sharefiles/' + nickname + '/' + itemID
if os.path.isfile(itemIDfile + '.' + ext): formats = getImageExtensions()
os.remove(itemIDfile + '.' + ext) for ext in formats:
saveJson(sharesJson, sharesFilename) if os.path.isfile(itemIDfile + '.' + ext):
os.remove(itemIDfile + '.' + ext)
saveJson(sharesJson, sharesFilename)
def getSharesFeedForPerson(baseDir: str, def getSharesFeedForPerson(baseDir: str,
@ -410,7 +416,8 @@ def sendShareViaServer(baseDir, session,
itemQty: int, itemType: str, itemCategory: str, itemQty: int, itemType: str, itemCategory: str,
location: str, duration: str, location: str, duration: str,
cachedWebfingers: {}, personCache: {}, cachedWebfingers: {}, personCache: {},
debug: bool, projectVersion: str) -> {}: debug: bool, projectVersion: str,
itemPrice: str, itemCurrency: str) -> {}:
"""Creates an item share via c2s """Creates an item share via c2s
""" """
if not session: if not session:
@ -438,6 +445,8 @@ def sendShareViaServer(baseDir, session,
"category": itemCategory, "category": itemCategory,
"location": location, "location": location,
"duration": duration, "duration": duration,
"itemPrice": itemPrice,
"itemCurrency": itemCurrency,
'to': [toUrl], 'to': [toUrl],
'cc': [ccUrl] 'cc': [ccUrl]
}, },
@ -663,7 +672,10 @@ def outboxShareUpload(baseDir: str, httpPrefix: str,
messageJson['object']['itemCategory'], messageJson['object']['itemCategory'],
messageJson['object']['location'], messageJson['object']['location'],
messageJson['object']['duration'], messageJson['object']['duration'],
debug, city, systemLanguage, translate) debug, city,
messageJson['object']['itemPrice'],
messageJson['object']['itemCurrency'],
systemLanguage, translate)
if debug: if debug:
print('DEBUG: shared item received via c2s') print('DEBUG: shared item received via c2s')
@ -695,3 +707,112 @@ def outboxUndoShareUpload(baseDir: str, httpPrefix: str,
messageJson['object']['displayName']) messageJson['object']['displayName'])
if debug: if debug:
print('DEBUG: shared item removed via c2s') print('DEBUG: shared item removed via c2s')
def sharesCatalogAccountEndpoint(baseDir: str, httpPrefix: str,
nickname: str, domain: str,
domainFull: str,
path: str) -> {}:
"""Returns the endpoint for the shares catalog of a particular account
"""
dfcUrl = \
"http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#"
owner = httpPrefix + '://' + domainFull + '/users/' + nickname
dfcInstanceId = owner + '/catalog'
endpoint = {
"@context": {
"DFC": dfcUrl,
"@base": "http://maPlateformeNationale"
},
"@id": dfcInstanceId,
"@type": "DFC:Entreprise",
"DFC:supplies": []
}
sharesFilename = acctDir(baseDir, nickname, domain) + '/shares.json'
if not os.path.isfile(sharesFilename):
return endpoint
sharesJson = loadJson(sharesFilename)
if not sharesJson:
return endpoint
for itemID, item in sharesJson.items():
if not item.get('dfcId'):
continue
expireDate = datetime.datetime.fromtimestamp(item['durationSec'])
expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ")
catalogItem = {
"@id": item['dfcId'],
"DFC:offeredThrough": owner,
"DFC:startDate": item['published'],
"DFC:expiryDate": expireDateStr,
"DFC:quantity": item['itemQty'],
"DFC:totalTheoriticalStock": item['itemQty'],
"DFC:price": "0",
"DFC:Image": item['imageUrl'],
"DFC:description": item['displayName'] + ': ' + item['summary']
}
endpoint['DFC:supplies'].append(catalogItem)
return endpoint
def sharesCatalogEndpoint(baseDir: str, httpPrefix: str,
domainFull: str,
path: str) -> {}:
"""Returns the endpoint for the shares catalog for the instance
"""
dfcUrl = \
"http://static.datafoodconsortium.org/ontologies/DFC_FullModel.owl#"
dfcInstanceId = httpPrefix + '://' + domainFull + '/catalog'
endpoint = {
"@context": {
"DFC": dfcUrl,
"@base": "http://maPlateformeNationale"
},
"@id": dfcInstanceId,
"@type": "DFC:Entreprise",
"DFC:supplies": []
}
for subdir, dirs, files in os.walk(baseDir + '/accounts'):
for acct in dirs:
if not isAccountDir(acct):
continue
nickname = acct.split('@')[0]
domain = acct.split('@')[1]
owner = httpPrefix + '://' + domainFull + '/users/' + nickname
sharesFilename = \
acctDir(baseDir, nickname, domain) + '/shares.json'
if not os.path.isfile(sharesFilename):
continue
sharesJson = loadJson(sharesFilename)
if not sharesJson:
continue
for itemID, item in sharesJson.items():
if not item.get('dfcId'):
continue
expireDate = \
datetime.datetime.fromtimestamp(item['durationSec'])
expireDateStr = expireDate.strftime("%Y-%m-%dT%H:%M:%SZ")
description = item['displayName'] + ': ' + item['summary']
catalogItem = {
"@id": item['dfcId'],
"DFC:offeredThrough": owner,
"DFC:startDate": item['published'],
"DFC:expiryDate": expireDateStr,
"DFC:quantity": item['itemQty'],
"DFC:totalTheoriticalStock": item['itemQty'],
"DFC:price": "0",
"DFC:Image": item['imageUrl'],
"DFC:description": description
}
endpoint['DFC:supplies'].append(catalogItem)
return endpoint

View File

@ -3272,7 +3272,9 @@ def _testFunctions():
'E2EEremoveDevice', 'E2EEremoveDevice',
'setOrganizationScheme', 'setOrganizationScheme',
'fill_headers', 'fill_headers',
'_nothing' '_nothing',
'sharesCatalogEndpoint',
'sharesCatalogAccountEndpoint'
] ]
excludeImports = [ excludeImports = [
'link', 'link',

View File

@ -454,5 +454,7 @@
"Languages": "اللغات", "Languages": "اللغات",
"Translated": "تترجم", "Translated": "تترجم",
"Quantity": "كمية", "Quantity": "كمية",
"food": "غذاء" "food": "غذاء",
"Price": "السعر",
"Currency": "عملة"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Idiomes", "Languages": "Idiomes",
"Translated": "Traduït", "Translated": "Traduït",
"Quantity": "Quantitat", "Quantity": "Quantitat",
"food": "menjar" "food": "menjar",
"Price": "Preu",
"Currency": "Moneda"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Ieithoedd", "Languages": "Ieithoedd",
"Translated": "Chyfieithwyd", "Translated": "Chyfieithwyd",
"Quantity": "Symiau", "Quantity": "Symiau",
"food": "bwyd" "food": "bwyd",
"Price": "Prisia",
"Currency": "Harian"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Sprachen", "Languages": "Sprachen",
"Translated": "Übersetzt", "Translated": "Übersetzt",
"Quantity": "Menge", "Quantity": "Menge",
"food": "lebensmittel" "food": "lebensmittel",
"Price": "Preis",
"Currency": "Währung"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Languages", "Languages": "Languages",
"Translated": "Translated", "Translated": "Translated",
"Quantity": "Quantity", "Quantity": "Quantity",
"food": "food" "food": "food",
"Price": "Price",
"Currency": "Currency"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Idiomas", "Languages": "Idiomas",
"Translated": "Traducida", "Translated": "Traducida",
"Quantity": "Cantidad", "Quantity": "Cantidad",
"food": "comida" "food": "comida",
"Price": "Precio",
"Currency": "Divisa"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Langues", "Languages": "Langues",
"Translated": "Traduite", "Translated": "Traduite",
"Quantity": "Quantité", "Quantity": "Quantité",
"food": "aliments" "food": "aliments",
"Price": "Prix",
"Currency": "Devise"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Teangacha", "Languages": "Teangacha",
"Translated": "Aistrithe", "Translated": "Aistrithe",
"Quantity": "Cainníocht", "Quantity": "Cainníocht",
"food": "bia" "food": "bia",
"Price": "Praghas a chur ar",
"Currency": "Airgeadra"
} }

View File

@ -454,5 +454,7 @@
"Languages": "बोली", "Languages": "बोली",
"Translated": "अनुवाद", "Translated": "अनुवाद",
"Quantity": "मात्रा", "Quantity": "मात्रा",
"food": "खाना" "food": "खाना",
"Price": "कीमत",
"Currency": "मुद्रा"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Le lingue", "Languages": "Le lingue",
"Translated": "Tradotto", "Translated": "Tradotto",
"Quantity": "Quantità", "Quantity": "Quantità",
"food": "cibo" "food": "cibo",
"Price": "Prezzo",
"Currency": "Moneta"
} }

View File

@ -454,5 +454,7 @@
"Languages": "言語", "Languages": "言語",
"Translated": "翻訳", "Translated": "翻訳",
"Quantity": "量", "Quantity": "量",
"food": "食物" "food": "食物",
"Price": "価格",
"Currency": "通貨"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Ziman", "Languages": "Ziman",
"Translated": "Wergerandin", "Translated": "Wergerandin",
"Quantity": "Jimarî", "Quantity": "Jimarî",
"food": "xûrek" "food": "xûrek",
"Price": "Biha",
"Currency": "Diravcins"
} }

View File

@ -450,5 +450,7 @@
"Languages": "Languages", "Languages": "Languages",
"Translated": "Translated", "Translated": "Translated",
"Quantity": "Quantity", "Quantity": "Quantity",
"food": "food" "food": "food",
"Price": "Price",
"Currency": "Currency"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Línguas", "Languages": "Línguas",
"Translated": "Traduzida", "Translated": "Traduzida",
"Quantity": "Quantidade", "Quantity": "Quantidade",
"food": "comida" "food": "comida",
"Price": "Preço",
"Currency": "Moeda"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Языки", "Languages": "Языки",
"Translated": "Перевод", "Translated": "Перевод",
"Quantity": "Количество", "Quantity": "Количество",
"food": "еда" "food": "еда",
"Price": "Цена",
"Currency": "Валюта"
} }

View File

@ -454,5 +454,7 @@
"Languages": "Lugha", "Languages": "Lugha",
"Translated": "Ilitafsiriwa", "Translated": "Ilitafsiriwa",
"Quantity": "Wingi", "Quantity": "Wingi",
"food": "chakula" "food": "chakula",
"Price": "Bei",
"Currency": "Fedha"
} }

View File

@ -454,5 +454,7 @@
"Languages": "语言", "Languages": "语言",
"Translated": "翻译", "Translated": "翻译",
"Quantity": "数量", "Quantity": "数量",
"food": "食物" "food": "食物",
"Price": "价钱",
"Currency": "货币"
} }

View File

@ -19,6 +19,7 @@ from webapp_utils import htmlHeaderWithExternalStyle
from webapp_utils import htmlFooter from webapp_utils import htmlFooter
from webapp_utils import editTextField from webapp_utils import editTextField
from webapp_utils import editNumberField from webapp_utils import editNumberField
from webapp_utils import editCurrencyField
def _htmlFollowingDataList(baseDir: str, nickname: str, def _htmlFollowingDataList(baseDir: str, nickname: str,
@ -371,6 +372,13 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {},
cityOrLocStr = translate['City or location of the shared item'] cityOrLocStr = translate['City or location of the shared item']
extraFields += editTextField(cityOrLocStr + ':', 'location', '') extraFields += editTextField(cityOrLocStr + ':', 'location', '')
extraFields += '</div>\n' extraFields += '</div>\n'
extraFields += '<div class="container">\n'
extraFields += \
editCurrencyField(translate['Price'] + ':', 'itemPrice', '0.00')
extraFields += '<br>' + \
editTextField(translate['Currency'] + ':',
'itemCurrency', 'EUR')
extraFields += '</div>\n'
citationsStr = '' citationsStr = ''
if endpoint == 'newblog': if endpoint == 'newblog':

View File

@ -188,7 +188,16 @@ def htmlSearchSharedItems(cssCache: {}, translate: {},
':</b> ' + sharedItem['category'] + ' ' ':</b> ' + sharedItem['category'] + ' '
sharedItemsForm += \ sharedItemsForm += \
'<b>' + translate['Location'] + \ '<b>' + translate['Location'] + \
':</b> ' + sharedItem['location'] + '</p>\n' ':</b> ' + sharedItem['location']
if sharedItem.get('itemPrice') and \
sharedItem.get('itemCurrency'):
if sharedItem['itemPrice'].isdigit():
if float(sharedItem['itemPrice']) > 0:
sharedItemsForm += \
' <b>' + translate['Price'] + \
':</b> ' + sharedItem['itemPrice'] + \
' ' + sharedItem['itemCurrency']
sharedItemsForm += '</p>\n'
contactActor = \ contactActor = \
httpPrefix + '://' + domainFull + \ httpPrefix + '://' + domainFull + \
'/users/' + contactNickname '/users/' + contactNickname

View File

@ -830,7 +830,14 @@ def htmlIndividualShare(actor: str, item: {}, translate: {},
profileStr += \ profileStr += \
'<b>' + translate['Category'] + ':</b> ' + item['category'] + ' ' '<b>' + translate['Category'] + ':</b> ' + item['category'] + ' '
profileStr += \ profileStr += \
'<b>' + translate['Location'] + ':</b> ' + item['location'] + '</p>\n' '<b>' + translate['Location'] + ':</b> ' + item['location']
if item.get('itemPrice') and item.get('itemCurrency'):
if item['itemPrice'].isdigit():
if float(item['itemPrice']) > 0:
profileStr += ' ' + \
'<b>' + translate['Price'] + ':</b> ' + \
item['itemPrice'] + ' ' + item['itemCurrency']
profileStr += '</p>\n'
sharedesc = item['displayName'] sharedesc = item['displayName']
if '<' not in sharedesc and '?' not in sharedesc: if '<' not in sharedesc and '?' not in sharedesc:
if showContact: if showContact:

View File

@ -1159,6 +1159,23 @@ def editNumberField(label: str, name: str, value: int = 1,
'min="' + str(minValue) + '" max="' + str(maxValue) + '" step="1">\n' 'min="' + str(minValue) + '" max="' + str(maxValue) + '" step="1">\n'
def editCurrencyField(label: str, name: str, value: str = "0.00",
placeholder: str = "0.00") -> str:
"""Returns html for editing a currency field
"""
if value is None:
value = '0.00'
placeholderStr = '0.00'
if placeholder:
if placeholder.isdigit():
placeholderStr = ' placeholder="' + str(placeholder) + '"'
return \
'<label class="labels">' + label + '</label><br>\n' + \
' <input type="text" name="' + name + '" value="' + \
str(value) + '"' + placeholderStr + ' ' + \
'" pattern="^\\d{1,3}(,\\d{3})*(\\.\\d+)?" data-type="currency">\n'
def editCheckBox(label: str, name: str, checked: bool = False) -> str: def editCheckBox(label: str, name: str, checked: bool = False) -> str:
"""Returns html for editing a checkbox field """Returns html for editing a checkbox field
""" """