Merge branch 'develop/0.1.0' into 'feature/8-create-docker-build'
# Conflicts: # springboot/Dockerfile
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Kontor
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,129 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
@@ -0,0 +1,2 @@
|
||||
# kontor-bottle
|
||||
Kontor with Python Bottle Framework
|
||||
@@ -0,0 +1,19 @@
|
||||
# set base image (host OS)
|
||||
FROM python:3.8
|
||||
|
||||
# set the working directory in the container
|
||||
WORKDIR /code
|
||||
|
||||
# copy the dependencies file to the working directory
|
||||
COPY requirements.txt /code/
|
||||
|
||||
# copy the content of the local src directory to the working directory
|
||||
COPY src/ /code/
|
||||
|
||||
# install dependencies
|
||||
RUN pip install --upgrade pip && pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
# command to run on container start
|
||||
CMD [ "python", "./kontor.py" ]
|
||||
@@ -0,0 +1,2 @@
|
||||
pymongo
|
||||
bottle
|
||||
@@ -0,0 +1,193 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import comics.publisherDAO
|
||||
import comics.artistDAO
|
||||
import comics.comicDAO
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.publishers = publisherDAO.PublisherDAO(database)
|
||||
self.artists = artistDAO.ArtistDAO(database)
|
||||
self.comics = comicDAO.ComicDAO(database)
|
||||
self.routing()
|
||||
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/comics/', 'GET', self.comic_index)
|
||||
self.app.route('/comics/comic', 'GET', self.comic_list)
|
||||
self.app.route('/comics/comic/<id>', 'GET', self.comic_details)
|
||||
self.app.route('/comics/comic/create', 'GET', self.get_comic_create)
|
||||
self.app.route('/comics/comic/create', 'POST', self.post_create_comic)
|
||||
self.app.route('/comics/publisher', 'GET', self.publisher_list)
|
||||
self.app.route('/comics/publisher/<id>', 'GET', self.publisher_details)
|
||||
self.app.route('/comics/publisher/create', 'GET', self.get_publisher_create)
|
||||
self.app.route('/comics/publisher/create', 'POST', self.post_create_publisher)
|
||||
self.app.route('/comics/artist', 'GET', self.artist_list)
|
||||
self.app.route('/comics/artist/<id>', 'GET', self.artist_details)
|
||||
self.app.route('/comics/artist/create', 'GET', self.get_artist_create)
|
||||
self.app.route('/comics/artist/create', 'POST', self.post_create_artist)
|
||||
self.app.route('/comics/storyarc', 'GET', self.storyarc_list)
|
||||
self.app.route('/comics/storyarc/<id>', 'GET', self.storyarc_details)
|
||||
self.app.route('/comics/storyarc/create', 'GET', self.get_storyarc_create)
|
||||
self.app.route('/comics/storyarc/create', 'POST', self.post_create_storyarc)
|
||||
|
||||
def comic_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('comic_index', dict(username=username))
|
||||
|
||||
|
||||
def comic_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.comics.get_comics()
|
||||
return bottle.template('comic_list', dict(comics=l, username=username))
|
||||
|
||||
|
||||
def comic_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
comic = self.comics.get_comic(id)
|
||||
errors = ""
|
||||
if comic == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('comic_template', dict(title=comic['title'],
|
||||
id=comic['_id'],
|
||||
current_order=comic['current_order'],
|
||||
completed=comic['completed'],
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def get_comic_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("comic_template", dict(title="",
|
||||
id='newentry',
|
||||
current_order=False,
|
||||
completed=False,
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def post_create_comic(self):
|
||||
comic_id = bottle.request.forms.get("id")
|
||||
comic_title = bottle.request.forms.get("title")
|
||||
comic_order = bottle.request.forms.get("current_order")
|
||||
comic_completed = bottle.request.forms.get("completed")
|
||||
if comic_id == "newentry":
|
||||
self.comics.insert_entry(comic_title, None, comic_order, comic_completed)
|
||||
else:
|
||||
self.comics.update_entry(comic_id, comic_title, None, comic_order, comic_completed)
|
||||
bottle.redirect("/comics/comic")
|
||||
|
||||
|
||||
def publisher_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.publishers.get_publishers()
|
||||
return bottle.template('publisher_list', dict(publishers=l, username=username))
|
||||
|
||||
|
||||
def publisher_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher = self.publishers.get_publisher(id)
|
||||
errors = ""
|
||||
if publisher == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('publisher_template', dict(name=publisher['name'], id=publisher['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_publisher_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("publisher_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_publisher(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher_id = bottle.request.forms.get("id")
|
||||
publisher_name = bottle.request.forms.get("name")
|
||||
if publisher_id == "newentry":
|
||||
self.publishers.insert_entry(publisher_name)
|
||||
else:
|
||||
self.publishers.update_entry(publisher_id, publisher_name)
|
||||
bottle.redirect("/comics/publisher")
|
||||
|
||||
|
||||
def artist_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.artists.get_artists()
|
||||
return bottle.template('artist_list', dict(artists=l, username=username))
|
||||
|
||||
|
||||
def artist_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
artist = self.artists.get_artist(id)
|
||||
errors = ""
|
||||
if artist == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('artist_template', dict(name=artist['name'], id=artist['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_artist_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("artist_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_artist(self):
|
||||
artist_id = bottle.request.forms.get("id")
|
||||
artist_name = bottle.request.forms.get("name")
|
||||
if artist_id == "newentry":
|
||||
self.artists.insert_entry(artist_name)
|
||||
else:
|
||||
self.artists.update_entry(artist_id, artist_name)
|
||||
bottle.redirect("/comics/artist")
|
||||
|
||||
|
||||
def storyarc_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.storyarcs.get_storyarcs()
|
||||
return bottle.template('storyarc_list', dict(storyarcs=l, username=username))
|
||||
|
||||
|
||||
def storyarc_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
storyarc = self.storyarcs.get_storyarc(id)
|
||||
errors = ""
|
||||
if storyarc == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('storyarc_template', dict(title=storyarc['title'], id=storyarc['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_storyarc_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("storyarc_template", dict(title="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_storyarc(self):
|
||||
storyarc_id = bottle.request.forms.get("id")
|
||||
storyarc_title = bottle.request.forms.get("title")
|
||||
if storyarc_id == "newentry":
|
||||
self.storyarcs.insert_entry(storyarc_title)
|
||||
else:
|
||||
self.storyarcs.update_entry(storyarc_id, storyarc_title)
|
||||
bottle.redirect("/comics/storyarc")
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ArtistDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.artists = database.artists
|
||||
|
||||
def get_artists(self):
|
||||
cursor = self.artists.find()
|
||||
l = []
|
||||
for artist in cursor:
|
||||
l.append({'name':artist['name'], '_id':artist['_id']})
|
||||
return l
|
||||
|
||||
def get_artist(self, artist_id):
|
||||
artist = self.artists.find_one({"_id": ObjectId(artist_id)})
|
||||
return artist
|
||||
|
||||
def insert_entry(self, artist_name):
|
||||
print("inserting artist entry", artist_name)
|
||||
|
||||
artist = {"name": artist_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.insert_one(artist)
|
||||
print("Matching artist: ", result.matched_count)
|
||||
print("Modified artist: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting artist")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, artist_id, artist_name):
|
||||
print("upserting artist entry", artist_name)
|
||||
|
||||
filter_doc = {"_id": ObjectId(artist_id)}
|
||||
artist = { "$set": {"name": artist_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.update_one(filter_doc, artist)
|
||||
print("Modified artist: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting artist")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,52 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ComicDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.comics = database.comics
|
||||
|
||||
def get_comics(self):
|
||||
cursor = self.comics.find()
|
||||
l = []
|
||||
for comic in cursor:
|
||||
l.append({'title':comic['title'],
|
||||
'_id':comic['_id'],
|
||||
'current_order':comic['current_order'],
|
||||
'completed':comic['completed']})
|
||||
return l
|
||||
|
||||
def get_comic(self, comic_id):
|
||||
comic = self.comics.find_one({"_id": ObjectId(comic_id)})
|
||||
return comic
|
||||
|
||||
def insert_entry(self, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print("inserting comic entry", comic_title)
|
||||
|
||||
comic = {"title": comic_title, "current_order": comic_order, "completed": comic_completed}
|
||||
|
||||
# now insert the comic
|
||||
try:
|
||||
result = self.comics.insert_one(comic)
|
||||
print("Modified comic: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting comic")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, comic_id, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print("upserting comic entry", comic_title)
|
||||
|
||||
filter_doc = {"_id": ObjectId(comic_id)}
|
||||
comic = { "$set": {"title": comic_title, 'current_order': comic_order, 'completed': comic_completed}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.comics.update_one(filter_doc, comic)
|
||||
print("Matching comic: ", result.matched_count)
|
||||
print("Modified comic: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting comic")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from mongoengine import connect, Document
|
||||
from mongoengine import StringField, BooleanField
|
||||
from mongoengine import ListField, ReferenceField, GenericReferenceField
|
||||
import pprint
|
||||
|
||||
|
||||
class Publisher(Document):
|
||||
name = StringField(required=True)
|
||||
|
||||
def __str__(self):
|
||||
s = "Publisher(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Artist(Document):
|
||||
name = StringField(required=True)
|
||||
className = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "Artist(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Issue(Document):
|
||||
number = StringField()
|
||||
comic = GenericReferenceField()
|
||||
is_read = BooleanField(default=False)
|
||||
is_stock = BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
s = "Issue(%s # %s, %s)" % (self.comic.title, self.number, self.is_read)
|
||||
return s
|
||||
|
||||
|
||||
class StoryArc(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "StoryArc(%s, %s)" % (self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Volume(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "Volume(%s, %s, %s)" % (self.id, self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Comic(Document):
|
||||
title = StringField(required=True)
|
||||
publisher = ReferenceField(Publisher)
|
||||
current_order = BooleanField()
|
||||
completed = BooleanField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
stories = ListField(ReferenceField(StoryArc))
|
||||
|
||||
def __str__(self):
|
||||
if self.publisher is None:
|
||||
s = "Comic(%s, %s, %s, %s)" % (self.title, self.publisher, self.current_order, self.completed)
|
||||
return s
|
||||
else:
|
||||
s = "Comic(%s, %s, %s, %s)" % (
|
||||
self.title, self.publisher.name, self.current_order, self.completed)
|
||||
return s
|
||||
|
||||
|
||||
class TradePaperback(Document):
|
||||
comic = ReferenceField(Comic)
|
||||
issue_start = StringField()
|
||||
issue_end = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "TPB(%s)" % self.comic.title
|
||||
return s
|
||||
|
||||
|
||||
def get_publisher(name):
|
||||
publisher = Publisher.objects(name=name)
|
||||
if publisher.count() > 0:
|
||||
return publisher[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_comic(title):
|
||||
comic = Comic.objects(title=title)
|
||||
if comic.count() > 0:
|
||||
return comic[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_issue(title, number):
|
||||
comic = get_comic(title)
|
||||
issues = Issue.objects(number=number, comic=comic)
|
||||
if issues.count() > 0:
|
||||
return issues[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
connect('comics')
|
||||
for publisher in Publisher.objects:
|
||||
pprint.pprint(publisher)
|
||||
for artist in Artist.objects:
|
||||
pprint.pprint(artist)
|
||||
for comic in Comic.objects:
|
||||
pprint.pprint(comic)
|
||||
for issue in Issue.objects:
|
||||
pprint.pprint(issue)
|
||||
for story in StoryArc.objects:
|
||||
pprint.pprint(story)
|
||||
for tpb in TradePaperback.objects:
|
||||
pprint.pprint(tpb)
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class PublisherDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.publishers = database.publishers
|
||||
|
||||
def get_publishers(self):
|
||||
cursor = self.publishers.find()
|
||||
l = []
|
||||
for publisher in cursor:
|
||||
l.append({'name':publisher['name'], '_id':publisher['_id']})
|
||||
return l
|
||||
|
||||
def get_publisher(self, publisher_id):
|
||||
publisher = self.publishers.find_one({"_id": ObjectId(publisher_id)})
|
||||
return publisher
|
||||
|
||||
def insert_entry(self, publisher_name):
|
||||
print("inserting publisher entry", publisher_name)
|
||||
|
||||
publisher = {"name": publisher_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.insert_one(publisher)
|
||||
print("Matching publisher: ", result.matched_count)
|
||||
print("Modified publisher: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting publisher")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, publisher_id, publisher_name):
|
||||
print("upserting publisher entry", publisher_name)
|
||||
|
||||
filter_doc = {"_id": ObjectId(publisher_id)}
|
||||
publisher = { "$set": {"name": publisher_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.update_one(filter_doc, publisher)
|
||||
print("Modified publisher: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting publisher")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
|
||||
class StoryArcDAO:
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.storyarcs = database.storyarcs
|
||||
|
||||
def get_storyarcs(self):
|
||||
cursor = self.storyarcs.find()
|
||||
l = []
|
||||
for storyarc in cursor:
|
||||
l.append({'title': storyarc['title'], '_id': storyarc['_id']})
|
||||
return l
|
||||
|
||||
def get_storyarc(self, storyarc_id):
|
||||
storyarc = self.storyarcs.find_one({"_id": ObjectId(storyarc_id)})
|
||||
return storyarc
|
||||
|
||||
def insert_entry(self, storyarc_title):
|
||||
print("inserting publisher entry", storyarc_title)
|
||||
|
||||
storyarc = {"name": storyarc_title}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.insert_one(storyarc)
|
||||
print("Matching storyarc: ", result.matched_count)
|
||||
print("Modified storyarc: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting storyarc")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
|
||||
def update_entry(self, storyarc_id, storyarc_title):
|
||||
print("upserting storyarc entry", storyarc_title)
|
||||
|
||||
filter_doc = {"_id": ObjectId(storyarc_id)}
|
||||
storyarc = {"$set": {"name": storyarc_title}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.update_one(filter_doc, storyarc)
|
||||
print("Modified storyarc: ", result.modified_count)
|
||||
except:
|
||||
print("Error inserting storyarc")
|
||||
print("Unexpected error:", sys.exc_info())
|
||||
@@ -0,0 +1,89 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
color: #333333;
|
||||
padding:4em 0 4em;
|
||||
}
|
||||
body,
|
||||
.wrapper {
|
||||
margin: 10px auto;
|
||||
/*max-width: 60em;*/
|
||||
}
|
||||
|
||||
header, nav, nav a, main, section, footer {
|
||||
border-radius: 0px 0.5em 0.5em;
|
||||
border: 1px solid;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
text-align:center;
|
||||
padding:10px;
|
||||
background: lightgrey;
|
||||
/*border-bottom: 1px solid #d5d5d5;*/
|
||||
}
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
padding-top: 10em;
|
||||
|
||||
font-size: 0.91em;
|
||||
float: left;
|
||||
width: 15em;
|
||||
padding: 0;
|
||||
background: lightskyblue;
|
||||
border-color: skyblue;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0.1em;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
padding: 0.2em 10px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
background-color: skyblue;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
nav ul a:hover,
|
||||
nav ul a:active {
|
||||
color: #fffbf0;
|
||||
background-color: #dfac20;
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
background: lightblue;
|
||||
border-color: #8a9da8;
|
||||
margin-left: 15em;
|
||||
min-width: 16em; /* Mindestbreite (der Überschrift) verhindert Anzeigefehler in modernen Browsern */
|
||||
}
|
||||
|
||||
footer {
|
||||
position:fixed;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
bottom:0;
|
||||
left: 0;
|
||||
right:0;
|
||||
background: lightgrey;
|
||||
border-color: grey;
|
||||
}
|
||||
|
||||
footer p {
|
||||
float:right;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymongo
|
||||
import sessionDAO
|
||||
import userDAO
|
||||
import comics
|
||||
#import library
|
||||
import bottle
|
||||
import cgi
|
||||
import re
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
app = bottle.Bottle()
|
||||
|
||||
|
||||
def index():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = sessions.get_username(cookie)
|
||||
return bottle.template('kontor', dict(username=username))
|
||||
|
||||
|
||||
def show_signup():
|
||||
return bottle.template("signup", dict(username="",
|
||||
password="",
|
||||
password_error="",
|
||||
email="",
|
||||
username_error="",
|
||||
email_error="",
|
||||
verify_error =""))
|
||||
|
||||
|
||||
def process_signup():
|
||||
email = bottle.request.forms.get("email")
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
verify = bottle.request.forms.get("verify")
|
||||
|
||||
# set these up in case we have an error case
|
||||
errors = {'username': cgi.escape(username), 'email': cgi.escape(email)}
|
||||
if validate_signup(username, password, verify, email, errors):
|
||||
|
||||
if not users.add_user(username, password, email):
|
||||
# this was a duplicate
|
||||
errors['username_error'] = "Username already in use. Please choose another"
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
session_id = sessions.start_session(username)
|
||||
print(session_id)
|
||||
bottle.response.set_cookie("session", session_id)
|
||||
bottle.redirect("/welcome")
|
||||
else:
|
||||
print("user did not validate")
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
|
||||
def show_login():
|
||||
return bottle.template('login', dict(username="", password="", login_error=""))
|
||||
|
||||
def process_login():
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
|
||||
print("user submitted ", username, "pass ", password)
|
||||
|
||||
user_record = users.validate_login(username, password)
|
||||
if user_record:
|
||||
# username is stored in the user collection in the _id key
|
||||
session_id = sessions.start_session(user_record['_id'])
|
||||
|
||||
if session_id is None:
|
||||
bottle.redirect("/internal_error")
|
||||
|
||||
cookie = session_id
|
||||
|
||||
# Warning, if you are running into a problem whereby the cookie being set here is
|
||||
# not getting set on the redirect, you are probably using the experimental version of bottle (.12).
|
||||
# revert to .11 to solve the problem.
|
||||
bottle.response.set_cookie("session", cookie)
|
||||
|
||||
bottle.redirect("/")
|
||||
|
||||
else:
|
||||
return bottle.template("login", dict(username=cgi.escape(username), password="", login_error="Invalid Login"))
|
||||
|
||||
|
||||
def process_logout():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
sessions.end_session(cookie)
|
||||
bottle.response.set_cookie("session", "")
|
||||
bottle.redirect("/")
|
||||
|
||||
|
||||
def send_stylesheet(filename):
|
||||
return bottle.static_file(filename, root='.', mimetype='text/css')
|
||||
|
||||
|
||||
def setup_routing(app):
|
||||
app.route('/', 'GET', index)
|
||||
app.route('/signup', 'GET', show_signup)
|
||||
app.route('/signup', 'POST', process_signup)
|
||||
app.route('/login', 'GET', show_login)
|
||||
app.route('/login', 'POST', process_login)
|
||||
app.route('/logout', 'GET', process_logout)
|
||||
app.route('/css/<filename:re:.*\.css>', 'GET', send_stylesheet)
|
||||
|
||||
|
||||
# validates that the user information is valid for new signup, return True of False
|
||||
# and fills in the error string if there is an issue
|
||||
def validate_signup(username, password, verify, email, errors):
|
||||
USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
|
||||
PASS_RE = re.compile(r"^.{3,20}$")
|
||||
EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")
|
||||
|
||||
errors['username_error'] = ""
|
||||
errors['password_error'] = ""
|
||||
errors['verify_error'] = ""
|
||||
errors['email_error'] = ""
|
||||
|
||||
if not USER_RE.match(username):
|
||||
errors['username_error'] = "invalid username. try just letters and numbers"
|
||||
return False
|
||||
|
||||
if not PASS_RE.match(password):
|
||||
errors['password_error'] = "invalid password."
|
||||
return False
|
||||
if password != verify:
|
||||
errors['verify_error'] = "password must match"
|
||||
return False
|
||||
if email != "":
|
||||
if not EMAIL_RE.match(email):
|
||||
errors['email_error'] = "invalid email address"
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
setup_routing(app)
|
||||
|
||||
connection = pymongo.MongoClient("mongodb://mongodb")
|
||||
database = connection.kontor
|
||||
|
||||
users = userDAO.UserDAO(database)
|
||||
sessions = sessionDAO.SessionDAO(database)
|
||||
|
||||
comics_plugin = comics.Plugin(app, database, sessions)
|
||||
#library_plugin = library.Plugin(app, database, sessions)
|
||||
print("starting app Kontor")
|
||||
bottle.run(app, host="0.0.0.0", port=9000, debug=True)
|
||||
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
# The session Data Access Object handles interactions with the sessions collection
|
||||
|
||||
class SessionDAO:
|
||||
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.sessions = database.sessions
|
||||
|
||||
# will start a new session id by adding a new document to the sessions collection
|
||||
# returns the sessionID or None
|
||||
def start_session(self, username):
|
||||
|
||||
session_id = self.get_random_str(32)
|
||||
session = {'username': username, '_id': session_id}
|
||||
|
||||
try:
|
||||
self.sessions.insert_one(session)
|
||||
except:
|
||||
print("Unexpected error on start_session:", sys.exc_info()[0])
|
||||
return None
|
||||
|
||||
return str(session['_id'])
|
||||
|
||||
# will send a new user session by deleting from sessions table
|
||||
def end_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return
|
||||
|
||||
self.sessions.delete_one({'_id': session_id})
|
||||
|
||||
return
|
||||
|
||||
# if there is a valid session, it is returned
|
||||
def get_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return None
|
||||
|
||||
session = self.sessions.find_one({'_id': session_id})
|
||||
|
||||
return session
|
||||
|
||||
# get the username of the current session, or None if the session is not valid
|
||||
def get_username(self, session_id):
|
||||
|
||||
session = self.get_session(session_id)
|
||||
if session is None:
|
||||
return None
|
||||
else:
|
||||
return session['username']
|
||||
|
||||
def get_random_str(self, num_chars):
|
||||
random_string = ""
|
||||
for i in range(num_chars):
|
||||
random_string = random_string + random.choice(string.ascii_letters)
|
||||
return random_string
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import hmac
|
||||
import random
|
||||
import string
|
||||
import hashlib
|
||||
import pymongo
|
||||
|
||||
|
||||
# The User Data Access Object handles all interactions with the User collection.
|
||||
class UserDAO:
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
self.users = self.db.users
|
||||
self.SECRET = 'verysecret'
|
||||
|
||||
# makes a little salt
|
||||
def make_salt(self):
|
||||
salt = ""
|
||||
for i in range(5):
|
||||
salt = salt + random.choice(string.ascii_letters)
|
||||
return salt
|
||||
|
||||
# implement the function make_pw_hash(name, pw) that returns a hashed password
|
||||
# of the format:
|
||||
# HASH(pw + salt),salt
|
||||
# use sha256
|
||||
|
||||
def make_pw_hash(self, pw,salt=None):
|
||||
if salt == None:
|
||||
salt = self.make_salt();
|
||||
return hashlib.sha256(pw + salt).hexdigest()+","+ salt
|
||||
|
||||
# Validates a user login. Returns user record or None
|
||||
def validate_login(self, username, password):
|
||||
|
||||
user = None
|
||||
try:
|
||||
user = self.users.find_one({'_id': username})
|
||||
except:
|
||||
print("Unable to query database for user")
|
||||
|
||||
if user is None:
|
||||
print("User not in database")
|
||||
return None
|
||||
|
||||
salt = user['password'].split(',')[1]
|
||||
|
||||
if user['password'] != self.make_pw_hash(password, salt):
|
||||
print("user password is not a match")
|
||||
return None
|
||||
|
||||
# Looks good
|
||||
return user
|
||||
|
||||
|
||||
# creates a new user in the users collection
|
||||
def add_user(self, username, password, email):
|
||||
password_hash = self.make_pw_hash(password)
|
||||
|
||||
user = {'_id': username, 'password': password_hash}
|
||||
if email != "":
|
||||
user['email'] = email
|
||||
|
||||
try:
|
||||
self.users.insert_one(user)
|
||||
except pymongo.errors.OperationFailure:
|
||||
print("oops, mongo error")
|
||||
return False
|
||||
except pymongo.errors.DuplicateKeyError as e:
|
||||
print("oops, username is already taken")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for artist in artists:
|
||||
<li><a href="/comics/artist/{{artist['_id']}}">{{artist['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/artist/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<table border="1">
|
||||
<tr><td>Title</td><td>Current Order</td><td>Completed</td></tr>
|
||||
<tr>
|
||||
%for comic in comics:
|
||||
<td><a href="/comics/comic/{{comic['_id']}}">{{comic['title']}}</a></td>
|
||||
<td>{{comic['current_order']}}</td>
|
||||
<td>{{comic['completed']}}</td>
|
||||
%end
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/comic/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="current_order" size="60" value="{{current_order}}">
|
||||
Current Order
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" name="completed" size="60" value="{{completed}}">
|
||||
Completed
|
||||
</label>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
%if (username == None):
|
||||
<footer><a href="/login">Login</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
%if (username != None):
|
||||
<footer><a href="/logout">{{username}}</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{login_error}}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for publisher in publishers:
|
||||
<li><a href="/comics/publisher/{{publisher['_id']}}">{{publisher['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/publisher/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>Signup</h2>
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error">{{username_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{password_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Verify Password</td>
|
||||
<td><input type="password" name="verify" value=""></td>
|
||||
<td class="error">{{verify_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Email (optional)</td>
|
||||
<td><input type="text" name="email" value="{{email}}"></td>
|
||||
<td class="error">{{email_error}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for storyarc in storyarcs:
|
||||
<li><a href="/comics/storyarc/{{storyarc['_id']}}">{{storyarc['title']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/storyarc/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo
|
||||
restart: always
|
||||
volumes:
|
||||
- db-data:/var/lib/mongo
|
||||
networks:
|
||||
- backend-network
|
||||
app:
|
||||
build: app
|
||||
restart: always
|
||||
ports:
|
||||
- 9000:9000
|
||||
networks:
|
||||
- backend-network
|
||||
- frontend-network
|
||||
nginx:
|
||||
image: nginx
|
||||
restart: always
|
||||
volumes:
|
||||
- ./nginx:/etc/nginx/conf.d
|
||||
- ./front-end:/var/www/front-end
|
||||
ports:
|
||||
- 8070:80
|
||||
networks:
|
||||
- frontend-network
|
||||
volumes:
|
||||
db-data:
|
||||
networks:
|
||||
backend-network:
|
||||
frontend-network:
|
||||
@@ -0,0 +1,82 @@
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Hello!</title>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
<style>
|
||||
*, ::after, ::before {
|
||||
box-sizing: inherit;
|
||||
background-color: #9eb5c2;
|
||||
color: rgb(8, 8, 71);
|
||||
}
|
||||
.moby {
|
||||
padding: 2rem 1rem;
|
||||
margin-bottom: 2rem;
|
||||
border-radius: .3rem;
|
||||
border-bottom: 0;
|
||||
text-align: center;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
padding: 4rem 2rem;
|
||||
}
|
||||
.moby button {
|
||||
color: #fff;
|
||||
background-color: rgb(6, 105, 138);
|
||||
border-color: rgb(6, 105, 138);
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
.moby .query {
|
||||
text-align: left;
|
||||
border: 3px dotted #e9ecef;
|
||||
padding: 1.5rem 1.2rem;
|
||||
max-width: 800px;
|
||||
min-height: 200px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-radius: .3rem;
|
||||
}
|
||||
.moby .query button:hover{
|
||||
cursor: pointer;
|
||||
background-color: #73abff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="moby">
|
||||
<img src="https://www.docker.com/sites/default/files/Whale%20Logo332_5.png" />
|
||||
<h1>Hello there!</h1>
|
||||
<p>Simple DEV environment setup with Docker and Docker Compose</p>
|
||||
<div class="query">
|
||||
<p>Set url path(default is '/'), then query the app service.</p>
|
||||
<div>
|
||||
<button type="submit" onclick="queryServer()">GET</button>
|
||||
<input id="path" type="text">
|
||||
</div>
|
||||
<h3>Server response</h3>
|
||||
<div id="response">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function queryServer() {
|
||||
const Http = new XMLHttpRequest();
|
||||
const path = document.getElementById('path').value;
|
||||
const url = "/";
|
||||
Http.open("GET", url + path);
|
||||
Http.send();
|
||||
|
||||
Http.onreadystatechange = (e) => {
|
||||
document.getElementById('response').innerHTML = Http.responseText;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,14 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /var/www/front-end;
|
||||
|
||||
location /api {
|
||||
proxy_pass http://app:9000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
.idea/
|
||||
__pycache__/
|
||||
bin/
|
||||
include/
|
||||
lib/
|
||||
lib64
|
||||
pip-selfcheck.json
|
||||
*.pyc
|
||||
kontor.properties
|
||||
*.nja
|
||||
dist/
|
||||
kontor_bottle.egg-info/
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
node {
|
||||
stage("Checkout") {
|
||||
checkout scm
|
||||
}
|
||||
stage("setup virualenv") {
|
||||
sh "virtualenv ."
|
||||
sh "source bin/activate; pip install bottle pymongo"
|
||||
}
|
||||
stage("build") {
|
||||
sh "source bin/activate; python setup.py sdist"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Kontor Bottle
|
||||
|
||||
@@ -0,0 +1,196 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import publisherDAO
|
||||
import artistDAO
|
||||
import comicDAO
|
||||
import storyArcDAO
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.publishers = publisherDAO.PublisherDAO(database)
|
||||
self.artists = artistDAO.ArtistDAO(database)
|
||||
self.comics = comicDAO.ComicDAO(database)
|
||||
self.storyarcs = storyArcDAO.StoryArcDAO(database)
|
||||
self.routing()
|
||||
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/comics', 'GET', self.comic_index)
|
||||
self.app.route('/comics/', 'GET', self.comic_index)
|
||||
self.app.route('/comics/comic', 'GET', self.comic_list)
|
||||
self.app.route('/comics/comic/<id>', 'GET', self.comic_details)
|
||||
self.app.route('/comics/comic/create', 'GET', self.get_comic_create)
|
||||
self.app.route('/comics/comic/create', 'POST', self.post_create_comic)
|
||||
self.app.route('/comics/publisher', 'GET', self.publisher_list)
|
||||
self.app.route('/comics/publisher/<id>', 'GET', self.publisher_details)
|
||||
self.app.route('/comics/publisher/create', 'GET', self.get_publisher_create)
|
||||
self.app.route('/comics/publisher/create', 'POST', self.post_create_publisher)
|
||||
self.app.route('/comics/artist', 'GET', self.artist_list)
|
||||
self.app.route('/comics/artist/<id>', 'GET', self.artist_details)
|
||||
self.app.route('/comics/artist/create', 'GET', self.get_artist_create)
|
||||
self.app.route('/comics/artist/create', 'POST', self.post_create_artist)
|
||||
self.app.route('/comics/storyarc', 'GET', self.storyarc_list)
|
||||
self.app.route('/comics/storyarc/<id>', 'GET', self.storyarc_details)
|
||||
self.app.route('/comics/storyarc/create', 'GET', self.get_storyarc_create)
|
||||
self.app.route('/comics/storyarc/create', 'POST', self.post_create_storyarc)
|
||||
|
||||
def comic_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('comic_index', dict(username=username))
|
||||
|
||||
|
||||
def comic_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.comics.get_comics()
|
||||
return bottle.template('comic_list', dict(comics=l, username=username))
|
||||
|
||||
|
||||
def comic_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
comic = self.comics.get_comic(id)
|
||||
errors = ""
|
||||
if comic == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('comic_template', dict(title=comic['title'],
|
||||
id=comic['_id'],
|
||||
current_order=comic['current_order'],
|
||||
completed=comic['completed'],
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def get_comic_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("comic_template", dict(title="",
|
||||
id='newentry',
|
||||
current_order=False,
|
||||
completed=False,
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
|
||||
def post_create_comic(self):
|
||||
comic_id = bottle.request.forms.get("id")
|
||||
comic_title = bottle.request.forms.get("title")
|
||||
comic_order = bottle.request.forms.get("current_order")
|
||||
comic_completed = bottle.request.forms.get("completed")
|
||||
if comic_id == "newentry":
|
||||
self.comics.insert_entry(comic_title, None, comic_order, comic_completed)
|
||||
else:
|
||||
self.comics.update_entry(comic_id, comic_title, None, comic_order, comic_completed)
|
||||
bottle.redirect("/comics/comic")
|
||||
|
||||
|
||||
def publisher_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.publishers.get_publishers()
|
||||
return bottle.template('publisher_list', dict(publishers=l, username=username))
|
||||
|
||||
|
||||
def publisher_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher = self.publishers.get_publisher(id)
|
||||
errors = ""
|
||||
if publisher == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('publisher_template', dict(name=publisher['name'], id=publisher['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_publisher_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("publisher_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_publisher(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
publisher_id = bottle.request.forms.get("id")
|
||||
publisher_name = bottle.request.forms.get("name")
|
||||
if publisher_id == "newentry":
|
||||
self.publishers.insert_entry(publisher_name)
|
||||
else:
|
||||
self.publishers.update_entry(publisher_id, publisher_name)
|
||||
bottle.redirect("/comics/publisher")
|
||||
|
||||
|
||||
def artist_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.artists.get_artists()
|
||||
return bottle.template('artist_list', dict(artists=l, username=username))
|
||||
|
||||
|
||||
def artist_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
artist = self.artists.get_artist(id)
|
||||
errors = ""
|
||||
if artist == None:
|
||||
errors= "Entry not found"
|
||||
return bottle.template('artist_template', dict(name=artist['name'], id=artist['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_artist_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("artist_template", dict(name="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_artist(self):
|
||||
artist_id = bottle.request.forms.get("id")
|
||||
artist_name = bottle.request.forms.get("name")
|
||||
if artist_id == "newentry":
|
||||
self.artists.insert_entry(artist_name)
|
||||
else:
|
||||
self.artists.update_entry(artist_id, artist_name)
|
||||
bottle.redirect("/comics/artist")
|
||||
|
||||
|
||||
def storyarc_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.storyarcs.get_storyarcs()
|
||||
return bottle.template('storyarc_list', dict(storyarcs=l, username=username))
|
||||
|
||||
|
||||
def storyarc_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
storyarc = self.storyarcs.get_storyarc(id)
|
||||
errors = ""
|
||||
if storyarc == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('storyarc_template', dict(title=storyarc['title'], id=storyarc['_id'], errors="", username=username))
|
||||
|
||||
|
||||
def get_storyarc_create(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("storyarc_template", dict(title="", id='newentry', errors="", username=username))
|
||||
|
||||
|
||||
def post_create_storyarc(self):
|
||||
storyarc_id = bottle.request.forms.get("id")
|
||||
storyarc_title = bottle.request.forms.get("title")
|
||||
if storyarc_id == "newentry":
|
||||
self.storyarcs.insert_entry(storyarc_title)
|
||||
else:
|
||||
self.storyarcs.update_entry(storyarc_id, storyarc_title)
|
||||
bottle.redirect("/comics/storyarc")
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ArtistDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.artists = database.artists
|
||||
|
||||
def get_artists(self):
|
||||
cursor = self.artists.find()
|
||||
l = []
|
||||
for artist in cursor:
|
||||
l.append({'name':artist['name'], '_id':artist['_id']})
|
||||
return l
|
||||
|
||||
def get_artist(self, artist_id):
|
||||
artist = self.artists.find_one({"_id": ObjectId(artist_id)})
|
||||
return artist
|
||||
|
||||
def insert_entry(self, artist_name):
|
||||
print "inserting artist entry", artist_name
|
||||
|
||||
artist = {"name": artist_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.insert_one(artist)
|
||||
print "Matching artist: ", result.matched_count
|
||||
print "Modified artist: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting artist"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
def update_entry(self, artist_id, artist_name):
|
||||
print "upserting artist entry", artist_name
|
||||
|
||||
filter_doc = {"_id": ObjectId(artist_id)}
|
||||
artist = { "$set": {"name": artist_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.artists.update_one(filter_doc, artist)
|
||||
print "Modified artist: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting artist"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class ComicDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.comics = database.comics
|
||||
|
||||
def get_comics(self):
|
||||
cursor = self.comics.find()
|
||||
l = []
|
||||
for comic in cursor:
|
||||
l.append({'title':comic['title'],
|
||||
'_id':comic['_id'],
|
||||
'current_order':comic['current_order'],
|
||||
'completed':comic['completed']})
|
||||
return l
|
||||
|
||||
def get_comic(self, comic_id):
|
||||
comic = self.comics.find_one({"_id": ObjectId(comic_id)})
|
||||
return comic
|
||||
|
||||
def insert_entry(self, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print "inserting comic entry", comic_title
|
||||
|
||||
comic = {"title": comic_title, "current_order": comic_order, "completed": comic_completed}
|
||||
|
||||
# now insert the comic
|
||||
try:
|
||||
result = self.comics.insert_one(comic)
|
||||
print "Modified comic: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting comic"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
def update_entry(self, comic_id, comic_title, comic_publisher=None, comic_order=False, comic_completed=False):
|
||||
print "upserting comic entry", comic_title
|
||||
|
||||
filter_doc = {"_id": ObjectId(comic_id)}
|
||||
comic = { "$set": {"title": comic_title, 'current_order': comic_order, 'completed': comic_completed}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.comics.update_one(filter_doc, comic)
|
||||
print "Matching comic: ", result.matched_count
|
||||
print "Modified comic: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting comic"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from mongoengine import connect, Document
|
||||
from mongoengine import StringField, BooleanField
|
||||
from mongoengine import ListField, ReferenceField, GenericReferenceField
|
||||
import pprint
|
||||
|
||||
|
||||
class Publisher(Document):
|
||||
name = StringField(required=True)
|
||||
|
||||
def __str__(self):
|
||||
s = "Publisher(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Artist(Document):
|
||||
name = StringField(required=True)
|
||||
className = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "Artist(%s)" % self.name
|
||||
return s
|
||||
|
||||
|
||||
class Issue(Document):
|
||||
number = StringField()
|
||||
comic = GenericReferenceField()
|
||||
is_read = BooleanField(default=False)
|
||||
is_stock = BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
s = "Issue(%s # %s, %s)" % (self.comic.title, self.number, self.is_read)
|
||||
return s
|
||||
|
||||
|
||||
class StoryArc(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "StoryArc(%s, %s)" % (self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Volume(Document):
|
||||
name = StringField(required=True)
|
||||
comic = GenericReferenceField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
|
||||
def __str__(self):
|
||||
s = "Volume(%s, %s, %s)" % (self.id, self.name, self.comic.title)
|
||||
return s
|
||||
|
||||
|
||||
class Comic(Document):
|
||||
title = StringField(required=True)
|
||||
publisher = ReferenceField(Publisher)
|
||||
current_order = BooleanField()
|
||||
completed = BooleanField()
|
||||
issues = ListField(ReferenceField(Issue))
|
||||
stories = ListField(ReferenceField(StoryArc))
|
||||
|
||||
def __str__(self):
|
||||
if self.publisher is None:
|
||||
s = "Comic(%s, %s, %s, %s)" % (self.title, self.publisher, self.current_order, self.completed)
|
||||
return s
|
||||
else:
|
||||
s = "Comic(%s, %s, %s, %s)" % (
|
||||
self.title, self.publisher.name, self.current_order, self.completed)
|
||||
return s
|
||||
|
||||
|
||||
class TradePaperback(Document):
|
||||
comic = ReferenceField(Comic)
|
||||
issue_start = StringField()
|
||||
issue_end = StringField()
|
||||
|
||||
def __str__(self):
|
||||
s = "TPB(%s)" % self.comic.title
|
||||
return s
|
||||
|
||||
|
||||
def get_publisher(name):
|
||||
publisher = Publisher.objects(name=name)
|
||||
if publisher.count() > 0:
|
||||
return publisher[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_comic(title):
|
||||
comic = Comic.objects(title=title)
|
||||
if comic.count() > 0:
|
||||
return comic[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def get_issue(title, number):
|
||||
comic = get_comic(title)
|
||||
issues = Issue.objects(number=number, comic=comic)
|
||||
if issues.count() > 0:
|
||||
return issues[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
if __name__ == '__main__':
|
||||
connect('comics')
|
||||
for publisher in Publisher.objects:
|
||||
pprint.pprint(publisher)
|
||||
for artist in Artist.objects:
|
||||
pprint.pprint(artist)
|
||||
for comic in Comic.objects:
|
||||
pprint.pprint(comic)
|
||||
for issue in Issue.objects:
|
||||
pprint.pprint(issue)
|
||||
for story in StoryArc.objects:
|
||||
pprint.pprint(story)
|
||||
for tpb in TradePaperback.objects:
|
||||
pprint.pprint(tpb)
|
||||
@@ -0,0 +1,50 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class PublisherDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.publishers = database.publishers
|
||||
|
||||
def get_publishers(self):
|
||||
cursor = self.publishers.find()
|
||||
l = []
|
||||
for publisher in cursor:
|
||||
l.append({'name':publisher['name'], '_id':publisher['_id']})
|
||||
return l
|
||||
|
||||
def get_publisher(self, publisher_id):
|
||||
publisher = self.publishers.find_one({"_id": ObjectId(publisher_id)})
|
||||
return publisher
|
||||
|
||||
def insert_entry(self, publisher_name):
|
||||
print "inserting publisher entry", publisher_name
|
||||
|
||||
publisher = {"name": publisher_name}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.insert_one(publisher)
|
||||
print "Matching publisher: ", result.matched_count
|
||||
print "Modified publisher: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting publisher"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
def update_entry(self, publisher_id, publisher_name):
|
||||
print "upserting publisher entry", publisher_name
|
||||
|
||||
filter_doc = {"_id": ObjectId(publisher_id)}
|
||||
publisher = { "$set": {"name": publisher_name}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.publishers.update_one(filter_doc, publisher)
|
||||
print "Modified publisher: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting publisher"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
|
||||
class StoryArcDAO:
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.storyarcs = database.storyarcs
|
||||
|
||||
def get_storyarcs(self):
|
||||
cursor = self.storyarcs.find()
|
||||
l = []
|
||||
for storyarc in cursor:
|
||||
l.append({'title': storyarc['title'], '_id': storyarc['_id']})
|
||||
return l
|
||||
|
||||
def get_storyarc(self, storyarc_id):
|
||||
storyarc = self.storyarcs.find_one({"_id": ObjectId(storyarc_id)})
|
||||
return storyarc
|
||||
|
||||
def insert_entry(self, storyarc_title):
|
||||
print "inserting publisher entry", storyarc_title
|
||||
|
||||
storyarc = {"name": storyarc_title}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.insert_one(storyarc)
|
||||
print "Matching storyarc: ", result.matched_count
|
||||
print "Modified storyarc: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting storyarc"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
def update_entry(self, storyarc_id, storyarc_title):
|
||||
print "upserting storyarc entry", storyarc_title
|
||||
|
||||
filter_doc = {"_id": ObjectId(storyarc_id)}
|
||||
storyarc = {"$set": {"name": storyarc_title}}
|
||||
|
||||
# now insert the post
|
||||
try:
|
||||
result = self.storyarcs.update_one(filter_doc, storyarc)
|
||||
print "Modified storyarc: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting storyarc"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.routing()
|
||||
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/office', 'GET', self.office_index)
|
||||
self.app.route('/office/travel', 'GET', self.office_index)
|
||||
|
||||
|
||||
def office_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('office_index', dict(username=username))
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
color: #333333;
|
||||
padding: 3em 0 4em;
|
||||
}
|
||||
|
||||
body,
|
||||
.wrapper {
|
||||
margin: 10px auto;
|
||||
/*max-width: 60em;*/
|
||||
}
|
||||
|
||||
header, main, section, footer {
|
||||
border-radius: 0px 0.5em 0.5em;
|
||||
border: 1px solid;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
text-align:center;
|
||||
padding: 10px;
|
||||
background: lightgrey;
|
||||
/*border-bottom: 1px solid #d5d5d5;*/
|
||||
}
|
||||
|
||||
nav {
|
||||
/*position: fixed;*/
|
||||
/*padding-top: 10em; */
|
||||
|
||||
border-radius: 0px 0.5em 0.5em;
|
||||
border: 1px solid;
|
||||
|
||||
padding: 0;
|
||||
margin: 10px;
|
||||
|
||||
font-size: 0.91em;
|
||||
float: left;
|
||||
width: 15em;
|
||||
background: lightskyblue;
|
||||
border-color: skyblue;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav li {
|
||||
list-style: none;
|
||||
margin: 0.4em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav ul ul {
|
||||
margin: 0 0 0 2em;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
nav ul ul li {
|
||||
margin: 0.3em 0;
|
||||
}
|
||||
|
||||
nav a {
|
||||
display: block;
|
||||
padding: 0.4em;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
border: 1px solid blue;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0px 5px 10px white inset;
|
||||
background-color: skyblue;
|
||||
color: #333;
|
||||
|
||||
}
|
||||
|
||||
nav a:focus,
|
||||
nav a:hover {
|
||||
color: royalblue;
|
||||
background-color: gold;
|
||||
}
|
||||
|
||||
main {
|
||||
display: block;
|
||||
background: lightblue;
|
||||
border-color: #8a9da8;
|
||||
margin-left: 15em;
|
||||
min-width: 16em; /* Mindestbreite (der Überschrift) verhindert Anzeigefehler in modernen Browsern */
|
||||
}
|
||||
|
||||
footer {
|
||||
position:fixed;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
bottom:0;
|
||||
left: 0;
|
||||
right:0;
|
||||
background: lightgrey;
|
||||
border-color: grey;
|
||||
}
|
||||
|
||||
footer p {
|
||||
float:right;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import pymongo
|
||||
import sessionDAO
|
||||
import userDAO
|
||||
import homeoffice
|
||||
import comics
|
||||
import library
|
||||
import medien
|
||||
import tradingcards
|
||||
import ConfigParser
|
||||
import bottle
|
||||
import cgi
|
||||
import re
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
app = bottle.Bottle()
|
||||
|
||||
|
||||
def index():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = sessions.get_username(cookie)
|
||||
return bottle.template('kontor', dict(username=username))
|
||||
|
||||
|
||||
def show_signup():
|
||||
return bottle.template("signup", dict(username="",
|
||||
password="",
|
||||
password_error="",
|
||||
email="",
|
||||
username_error="",
|
||||
email_error="",
|
||||
verify_error =""))
|
||||
|
||||
|
||||
def process_signup():
|
||||
email = bottle.request.forms.get("email")
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
verify = bottle.request.forms.get("verify")
|
||||
|
||||
# set these up in case we have an error case
|
||||
errors = {'username': cgi.escape(username), 'email': cgi.escape(email)}
|
||||
if validate_signup(username, password, verify, email, errors):
|
||||
|
||||
if not users.add_user(username, password, email):
|
||||
# this was a duplicate
|
||||
errors['username_error'] = "Username already in use. Please choose another"
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
session_id = sessions.start_session(username)
|
||||
print session_id
|
||||
bottle.response.set_cookie("session", session_id)
|
||||
bottle.redirect("/welcome")
|
||||
else:
|
||||
print "user did not validate"
|
||||
return bottle.template("signup", errors)
|
||||
|
||||
|
||||
def show_login():
|
||||
return bottle.template('login', dict(username="", password="", login_error=""))
|
||||
|
||||
def process_login():
|
||||
username = bottle.request.forms.get("username")
|
||||
password = bottle.request.forms.get("password")
|
||||
|
||||
print "user submitted ", username, "pass ", password
|
||||
|
||||
user_record = users.validate_login(username, password)
|
||||
if user_record:
|
||||
# username is stored in the user collection in the _id key
|
||||
session_id = sessions.start_session(user_record['_id'])
|
||||
|
||||
if session_id is None:
|
||||
bottle.redirect("/internal_error")
|
||||
|
||||
cookie = session_id
|
||||
|
||||
# Warning, if you are running into a problem whereby the cookie being set here is
|
||||
# not getting set on the redirect, you are probably using the experimental version of bottle (.12).
|
||||
# revert to .11 to solve the problem.
|
||||
bottle.response.set_cookie("session", cookie)
|
||||
|
||||
bottle.redirect("/")
|
||||
|
||||
else:
|
||||
return bottle.template("login", dict(username=cgi.escape(username), password="", login_error="Invalid Login"))
|
||||
|
||||
|
||||
def process_logout():
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
sessions.end_session(cookie)
|
||||
bottle.response.set_cookie("session", "")
|
||||
bottle.redirect("/")
|
||||
|
||||
|
||||
def send_stylesheet(filename):
|
||||
return bottle.static_file(filename, root='.', mimetype='text/css')
|
||||
|
||||
|
||||
def setup_routing(app):
|
||||
app.route('/', 'GET', index)
|
||||
app.route('/signup', 'GET', show_signup)
|
||||
app.route('/signup', 'POST', process_signup)
|
||||
app.route('/login', 'GET', show_login)
|
||||
app.route('/login', 'POST', process_login)
|
||||
app.route('/logout', 'GET', process_logout)
|
||||
app.route('/css/<filename:re:.*\.css>', 'GET', send_stylesheet)
|
||||
|
||||
|
||||
# validates that the user information is valid for new signup, return True of False
|
||||
# and fills in the error string if there is an issue
|
||||
def validate_signup(username, password, verify, email, errors):
|
||||
USER_RE = re.compile(r"^[a-zA-Z0-9_-]{3,20}$")
|
||||
PASS_RE = re.compile(r"^.{3,20}$")
|
||||
EMAIL_RE = re.compile(r"^[\S]+@[\S]+\.[\S]+$")
|
||||
|
||||
errors['username_error'] = ""
|
||||
errors['password_error'] = ""
|
||||
errors['verify_error'] = ""
|
||||
errors['email_error'] = ""
|
||||
|
||||
if not USER_RE.match(username):
|
||||
errors['username_error'] = "invalid username. try just letters and numbers"
|
||||
return False
|
||||
|
||||
if not PASS_RE.match(password):
|
||||
errors['password_error'] = "invalid password."
|
||||
return False
|
||||
if password != verify:
|
||||
errors['verify_error'] = "password must match"
|
||||
return False
|
||||
if email != "":
|
||||
if not EMAIL_RE.match(email):
|
||||
errors['email_error'] = "invalid email address"
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
setup_routing(app)
|
||||
|
||||
config = ConfigParser.ConfigParser()
|
||||
config.read('kontor.properties')
|
||||
server = config.get('host', 'server')
|
||||
port = config.get('host', 'port')
|
||||
|
||||
db_server = config.get('database', 'server')
|
||||
db_port = config.get('database', 'port')
|
||||
db_admin = config.get('database', 'adminDB')
|
||||
db_user = config.get('database', 'user')
|
||||
db_password = config.get('database', 'password')
|
||||
connection_string = "mongodb://"
|
||||
if db_admin:
|
||||
connection_string += db_user
|
||||
connection_string += ':'
|
||||
connection_string += db_password
|
||||
connection_string += '@'
|
||||
connection_string += db_server
|
||||
connection_string += '?/authsource='
|
||||
connection_string += db_admin
|
||||
else:
|
||||
connection_string += db_server
|
||||
connection = pymongo.MongoClient(connection_string)
|
||||
database = connection.kontor
|
||||
|
||||
users = userDAO.UserDAO(database)
|
||||
sessions = sessionDAO.SessionDAO(database)
|
||||
|
||||
office_plugin = homeoffice.Plugin(app, database, sessions)
|
||||
comics_plugin = comics.Plugin(app, database, sessions)
|
||||
library_plugin = library.Plugin(app, database, sessions)
|
||||
medien_plugin = medien.Plugin(app, database, sessions)
|
||||
tradingcards_plugin = tradingcards.Plugin(app, database, sessions)
|
||||
|
||||
bottle.run(app, host=server, port=port, debug=True, reloader=True)
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import bookDAO
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.books = bookDAO.BookDAO(database)
|
||||
self.routing()
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/library', 'GET', self.book_list)
|
||||
self.app.route('/library/book', 'GET', self.book_list)
|
||||
self.app.route('/library/book/<id>', 'GET', self.book_details)
|
||||
self.app.route('/library/book/create', 'GET', self.get_book_create)
|
||||
self.app.route('/library/book/create', 'POST', self.post_create_book)
|
||||
|
||||
def book_list(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
l = self.books.get_books()
|
||||
return bottle.template('book_list', dict(books=l, username=username))
|
||||
|
||||
def book_details(self, id):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
book = self.books.get_book(id)
|
||||
errors = ""
|
||||
if book == None:
|
||||
errors = "Entry not found"
|
||||
return bottle.template('book_template', dict(title=book['title'],
|
||||
id=book['_id'],
|
||||
current_order=book['current_order'],
|
||||
completed=book['completed'],
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
def get_book_create(self):
|
||||
cookie = bottle.rddequest.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template("book_template", dict(title="",
|
||||
id='newentry',
|
||||
current_order=False,
|
||||
completed=False,
|
||||
errors="",
|
||||
username=username))
|
||||
|
||||
def post_create_book(self):
|
||||
book_id = bottle.request.forms.get("id")
|
||||
book_title = bottle.request.forms.get("title")
|
||||
book_order = bottle.request.forms.get("current_order")
|
||||
book_completed = bottle.request.forms.get("completed")
|
||||
if book_id == "newentry":
|
||||
self.books.insert_entry(book_title, None, book_order, book_completed)
|
||||
else:
|
||||
self.comics.update_entry(book_id, book_title, None, book_order, book_completed)
|
||||
bottle.redirect("/comics/comic")
|
||||
@@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from bson.objectid import ObjectId
|
||||
import sys
|
||||
|
||||
class BookDAO:
|
||||
|
||||
# constructor for the class
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.books = database.books
|
||||
|
||||
def get_books(self):
|
||||
cursor = self.books.find()
|
||||
l = []
|
||||
for book in cursor:
|
||||
l.append(
|
||||
{'title':book['title'],
|
||||
'_id':book['_id'],
|
||||
'publisher':book['publisher'],
|
||||
'isbn':book['isbn'],
|
||||
'author':book['author']}
|
||||
)
|
||||
return l
|
||||
|
||||
def get_book(self, book_id):
|
||||
book = self.books.find_one({"_id": ObjectId(book_id)})
|
||||
return book
|
||||
|
||||
def insert_entry(self, book_title):
|
||||
print "inserting book entry", book_title
|
||||
|
||||
book = {"title": book_title}
|
||||
|
||||
# now insert the book
|
||||
try:
|
||||
result = self.books.insert_one(book)
|
||||
print "Matching book: ", result.matched_count
|
||||
print "Modified book: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting book"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
def update_entry(self, book_id, book_title):
|
||||
print "updating book entry", book_title
|
||||
|
||||
filter_doc = {"_id": ObjectId(book_id)}
|
||||
book = { "$set": {"name": book_title}}
|
||||
|
||||
# now insert the book
|
||||
try:
|
||||
result = self.books.update_one(filter_doc, book)
|
||||
print "Modified book: ", result.modified_count
|
||||
except:
|
||||
print "Error inserting book"
|
||||
print "Unexpected error:", sys.exc_info()
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.routing()
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/medien', 'GET', self.media_index)
|
||||
|
||||
def media_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('media_index', dict(username=username))
|
||||
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
# The session Data Access Object handles interactions with the sessions collection
|
||||
|
||||
class SessionDAO:
|
||||
|
||||
def __init__(self, database):
|
||||
self.db = database
|
||||
self.sessions = database.sessions
|
||||
|
||||
# will start a new session id by adding a new document to the sessions collection
|
||||
# returns the sessionID or None
|
||||
def start_session(self, username):
|
||||
|
||||
session_id = self.get_random_str(32)
|
||||
session = {'username': username, '_id': session_id}
|
||||
|
||||
try:
|
||||
self.sessions.insert_one(session)
|
||||
except:
|
||||
print "Unexpected error on start_session:", sys.exc_info()[0]
|
||||
return None
|
||||
|
||||
return str(session['_id'])
|
||||
|
||||
# will send a new user session by deleting from sessions table
|
||||
def end_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return
|
||||
|
||||
self.sessions.delete_one({'_id': session_id})
|
||||
|
||||
return
|
||||
|
||||
# if there is a valid session, it is returned
|
||||
def get_session(self, session_id):
|
||||
|
||||
if session_id is None:
|
||||
return None
|
||||
|
||||
session = self.sessions.find_one({'_id': session_id})
|
||||
|
||||
return session
|
||||
|
||||
# get the username of the current session, or None if the session is not valid
|
||||
def get_username(self, session_id):
|
||||
|
||||
session = self.get_session(session_id)
|
||||
if session is None:
|
||||
return None
|
||||
else:
|
||||
return session['username']
|
||||
|
||||
def get_random_str(self, num_chars):
|
||||
random_string = ""
|
||||
for i in range(num_chars):
|
||||
random_string = random_string + random.choice(string.ascii_letters)
|
||||
return random_string
|
||||
@@ -0,0 +1,11 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='kontor-bottle',
|
||||
packages=['comics', 'homeoffice', 'library', 'medien'],
|
||||
include_package_data=True,
|
||||
install_requires=[
|
||||
'bottle',
|
||||
'pymongo'
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding:utf-8 -*-
|
||||
import pymongo
|
||||
import bottle
|
||||
import cgi
|
||||
|
||||
|
||||
__author__ = 'tpeetz'
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
def __init__(self, app, database, sessions):
|
||||
self.app = app
|
||||
self.db = database
|
||||
self.sessions = sessions
|
||||
self.routing()
|
||||
|
||||
def routing(self):
|
||||
self.app.route('/tradingcards', 'GET', self.tradingcards_index)
|
||||
|
||||
def tradingcards_index(self):
|
||||
cookie = bottle.request.get_cookie("session")
|
||||
username = self.sessions.get_username(cookie)
|
||||
return bottle.template('tradingcards_index', dict(username=username))
|
||||
|
||||
Executable
+77
@@ -0,0 +1,77 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import hmac
|
||||
import random
|
||||
import string
|
||||
import hashlib
|
||||
import pymongo
|
||||
|
||||
|
||||
# The User Data Access Object handles all interactions with the User collection.
|
||||
class UserDAO:
|
||||
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
self.users = self.db.users
|
||||
self.SECRET = 'verysecret'
|
||||
|
||||
# makes a little salt
|
||||
def make_salt(self):
|
||||
salt = ""
|
||||
for i in range(5):
|
||||
salt = salt + random.choice(string.ascii_letters)
|
||||
return salt
|
||||
|
||||
# implement the function make_pw_hash(name, pw) that returns a hashed password
|
||||
# of the format:
|
||||
# HASH(pw + salt),salt
|
||||
# use sha256
|
||||
|
||||
def make_pw_hash(self, pw,salt=None):
|
||||
if salt == None:
|
||||
salt = self.make_salt();
|
||||
return hashlib.sha256(pw + salt).hexdigest()+","+ salt
|
||||
|
||||
# Validates a user login. Returns user record or None
|
||||
def validate_login(self, username, password):
|
||||
|
||||
user = None
|
||||
try:
|
||||
user = self.users.find_one({'_id': username})
|
||||
except:
|
||||
print "Unable to query database for user"
|
||||
|
||||
if user is None:
|
||||
print "User not in database"
|
||||
return None
|
||||
|
||||
salt = user['password'].split(',')[1]
|
||||
|
||||
if user['password'] != self.make_pw_hash(password, salt):
|
||||
print "user password is not a match"
|
||||
return None
|
||||
|
||||
# Looks good
|
||||
return user
|
||||
|
||||
|
||||
# creates a new user in the users collection
|
||||
def add_user(self, username, password, email):
|
||||
password_hash = self.make_pw_hash(password)
|
||||
|
||||
user = {'_id': username, 'password': password_hash}
|
||||
if email != "":
|
||||
user['email'] = email
|
||||
|
||||
try:
|
||||
self.users.insert_one(user)
|
||||
except pymongo.errors.OperationFailure:
|
||||
print "oops, mongo error"
|
||||
return False
|
||||
except pymongo.errors.DuplicateKeyError as e:
|
||||
print "oops, username is already taken"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for artist in artists:
|
||||
<li><a href="/comics/artist/{{artist['_id']}}">{{artist['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/artist/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<table border="1">
|
||||
<tr><td>Title</td><td>Current Order</td><td>Completed</td></tr>
|
||||
<tr>
|
||||
%for book in books:
|
||||
<td><a href="/library/book/{{book['_id']}}">{{book['title']}}</a></td>
|
||||
<td>{{book['current_order']}}</td>
|
||||
<td>{{book['completed']}}</td>
|
||||
%end
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<table border="1">
|
||||
<tr><td>Title</td><td>Current Order</td><td>Completed</td></tr>
|
||||
<tr>
|
||||
%for comic in comics:
|
||||
<td><a href="/comics/comic/{{comic['_id']}}">{{comic['title']}}</a></td>
|
||||
<td>{{comic['current_order']}}</td>
|
||||
<td>{{comic['completed']}}</td>
|
||||
%end
|
||||
</tr>
|
||||
</table>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/comic/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<label>
|
||||
<input type="checkbox" name="current_order" size="60" value="{{current_order}}">
|
||||
Current Order
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" name="completed" size="60" value="{{completed}}">
|
||||
Completed
|
||||
</label>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/office">HomeOffice</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
</details>
|
||||
</main>
|
||||
%if (username == None):
|
||||
<footer><a href="/login">Login</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
%if (username != None):
|
||||
<footer><a href="/logout">{{username}}</a><p>Ingenieurbüro Thomas Peetz</p></footer>
|
||||
%end
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form method="POST">
|
||||
<table>
|
||||
<tr>
|
||||
<td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error"></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{login_error}}</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/office">HomeOffice</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>Medien</h2>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/office">HomeOffice</a></li>
|
||||
<ul>
|
||||
<li><a href="/office/travel">Dienstreise</a></li>
|
||||
</ul>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>HomeOffice</h2>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for publisher in publishers:
|
||||
<li><a href="/comics/publisher/{{publisher['_id']}}">{{publisher['name']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/publisher/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="name" size="60" value="{{name}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>Signup</h2>
|
||||
<form method="post">
|
||||
<table>
|
||||
<tr><td class="label">Username</td>
|
||||
<td><input type="text" name="username" value="{{username}}"></td>
|
||||
<td class="error">{{username_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Password</td>
|
||||
<td><input type="password" name="password" value=""></td>
|
||||
<td class="error">{{password_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Verify Password</td>
|
||||
<td><input type="password" name="verify" value=""></td>
|
||||
<td class="error">{{verify_error}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label">Email (optional)</td>
|
||||
<td><input type="text" name="email" value="{{email}}"></td>
|
||||
<td class="error">{{email_error}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<input type="submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics/comic">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<ul>
|
||||
%for storyarc in storyarcs:
|
||||
<li><a href="/comics/storyarc/{{storyarc['_id']}}">{{storyarc['title']}}</a></li>
|
||||
%end
|
||||
</ul>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,43 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<ul>
|
||||
<li><a href="/comics/artist">Artists</a></li>
|
||||
<li><a href="/comics/publisher">Publishers</a></li>
|
||||
<li><a href="/comics/storyarc">StoryArcs</a></li>
|
||||
</ul>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<form action="/comics/storyarc/create" method="POST">
|
||||
{{errors}}
|
||||
<h2>Title</h2>
|
||||
<input type="hidden" name="id", value="{{id}}">
|
||||
<input type="text" name="title" size="60" value="{{title}}"><br>
|
||||
<p>
|
||||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Kontor</title>
|
||||
<link rel="stylesheet" href="/css/kontor.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>Kontor</header>
|
||||
<nav><ul>
|
||||
<li><a href="/">Kontor</a></li>
|
||||
<li><a href="/office">HomeOffice</a></li>
|
||||
<li><a href="/comics">Comics</a></li>
|
||||
<li><a href="/library">Bücher</a></li>
|
||||
<li><a href="/medien">Medien</a></li>
|
||||
<li><a href="/tradingcards">Trading Cards</a></li>
|
||||
</ul></nav>
|
||||
<main role="main">
|
||||
<details>
|
||||
<h2>Trading Cards</h2>
|
||||
</details>
|
||||
</main>
|
||||
<footer>
|
||||
%if (username == None):
|
||||
<a href="/login">Login</a>
|
||||
%end
|
||||
%if (username != None):
|
||||
<a href="/logout">{{username}}</a>
|
||||
%end
|
||||
<p>Ingenieurbüro Thomas Peetz</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
bin/
|
||||
include/
|
||||
lib/
|
||||
lib64/
|
||||
lib64
|
||||
pip-selfcheck.json
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
*.pyc
|
||||
kontor/db.sqlite3
|
||||
.idea/
|
||||
=======
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
*.pyc
|
||||
>>>>>>> ignore compiled scripts
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
node {
|
||||
stage "Checkout"
|
||||
checkout scm
|
||||
stage "make migrations"
|
||||
sh "python kontor/manage.py makemigrations"
|
||||
stage "execute tests"
|
||||
<<<<<<< HEAD
|
||||
sh "python kontor/manage.py test comics library medien tradingcards homeoffice"
|
||||
=======
|
||||
sh "python kontor/manage.py test commics library medien tradingcards homeoffice"
|
||||
>>>>>>> add tests
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
# Kontor Django
|
||||
|
||||
Executable
+86
@@ -0,0 +1,86 @@
|
||||
from django.contrib import admin
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> add Comic models
|
||||
from .models import Publisher
|
||||
from .models import Artist
|
||||
from .models import Issue
|
||||
from .models import StoryArc
|
||||
from .models import Comic
|
||||
from .models import TradePaperback
|
||||
from .models import Volume
|
||||
|
||||
<<<<<<< HEAD
|
||||
# Register your models here.
|
||||
|
||||
|
||||
class IssueInline(admin.StackedInline):
|
||||
model = Issue
|
||||
extra = 1
|
||||
|
||||
|
||||
class IssueAdmin(admin.ModelAdmin):
|
||||
fields = ['comic', 'number', 'is_read', 'in_stock']
|
||||
|
||||
|
||||
class ComicAdmin(admin.ModelAdmin):
|
||||
fields = ['title', 'publisher', 'writer', 'current_order', 'completed']
|
||||
inlines = [IssueInline]
|
||||
|
||||
|
||||
class ComicInline(admin.StackedInline):
|
||||
model = Comic
|
||||
extra = 1
|
||||
|
||||
|
||||
class PublisherAdmin(admin.ModelAdmin):
|
||||
inlines = [ComicInline]
|
||||
|
||||
|
||||
admin.site.register(Publisher, PublisherAdmin)
|
||||
admin.site.register(Artist)
|
||||
admin.site.register(Issue, IssueAdmin)
|
||||
admin.site.register(Volume)
|
||||
admin.site.register(StoryArc)
|
||||
admin.site.register(Comic, ComicAdmin)
|
||||
admin.site.register(TradePaperback)
|
||||
=======
|
||||
# Register your models here.
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
# Register your models here.
|
||||
|
||||
|
||||
class IssueInline(admin.StackedInline):
|
||||
model = Issue
|
||||
extra = 1
|
||||
|
||||
|
||||
class IssueAdmin(admin.ModelAdmin):
|
||||
fields = ['comic', 'number', 'is_read', 'in_stock']
|
||||
|
||||
|
||||
class ComicAdmin(admin.ModelAdmin):
|
||||
fields = ['title', 'publisher', 'writer', 'artist', 'current_order', 'completed']
|
||||
inlines = [IssueInline]
|
||||
|
||||
|
||||
class ComicInline(admin.StackedInline):
|
||||
model = Comic
|
||||
extra = 1
|
||||
|
||||
|
||||
class PublisherAdmin(admin.ModelAdmin):
|
||||
inlines = [ComicInline]
|
||||
|
||||
|
||||
admin.site.register(Publisher, PublisherAdmin)
|
||||
admin.site.register(Artist)
|
||||
admin.site.register(Issue, IssueAdmin)
|
||||
admin.site.register(Volume)
|
||||
admin.site.register(StoryArc)
|
||||
admin.site.register(Comic, ComicAdmin)
|
||||
admin.site.register(TradePaperback)
|
||||
>>>>>>> add Comic models
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ComicsConfig(AppConfig):
|
||||
name = 'comics'
|
||||
Executable
+3048
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,105 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
<<<<<<< HEAD
|
||||
# Generated by Django 1.9.6 on 2016-06-13 15:33
|
||||
=======
|
||||
# Generated by Django 1.9.5 on 2016-04-10 20:23
|
||||
>>>>>>> ignore compiled scripts
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Artist',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Comic',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=40)),
|
||||
('current_order', models.BooleanField()),
|
||||
('completed', models.BooleanField()),
|
||||
<<<<<<< HEAD
|
||||
('artist', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='artist', to='comics.Artist')),
|
||||
=======
|
||||
>>>>>>> ignore compiled scripts
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Issue',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('number', models.CharField(max_length=4)),
|
||||
('is_read', models.BooleanField(default=False)),
|
||||
('in_stock', models.BooleanField(default=False)),
|
||||
('comic', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='comics.Comic')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Publisher',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='StoryArc',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=100)),
|
||||
('comic', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='comics.Comic')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='TradePaperback',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=40)),
|
||||
('issue_start', models.IntegerField()),
|
||||
('issue_end', models.IntegerField()),
|
||||
('comic', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='comics.Comic')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Volume',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=40)),
|
||||
('comic', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comics.Comic')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
<<<<<<< HEAD
|
||||
model_name='issue',
|
||||
name='volume',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='comics.Volume'),
|
||||
),
|
||||
migrations.AddField(
|
||||
=======
|
||||
>>>>>>> ignore compiled scripts
|
||||
model_name='comic',
|
||||
name='publisher',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='comics.Publisher'),
|
||||
),
|
||||
<<<<<<< HEAD
|
||||
migrations.AddField(
|
||||
model_name='comic',
|
||||
name='writer',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='writer', to='comics.Artist'),
|
||||
),
|
||||
=======
|
||||
>>>>>>> ignore compiled scripts
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-06-20 14:05
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('comics', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='comic',
|
||||
name='artist',
|
||||
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='artist', to='comics.Artist'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='comic',
|
||||
name='writer',
|
||||
field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='writer', to='comics.Artist'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-06-20 14:10
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('comics', '0002_auto_20160620_1405'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='comic',
|
||||
name='artist',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='artist', to='comics.Artist'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='comic',
|
||||
name='writer',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='writer', to='comics.Artist'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-06-22 11:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('comics', '0003_auto_20160620_1410'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='issue',
|
||||
name='volume',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comics.Volume'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-06-22 11:46
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('comics', '0004_auto_20160622_1122'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='comic',
|
||||
name='title',
|
||||
field=models.CharField(max_length=60),
|
||||
),
|
||||
]
|
||||
Executable
+101
@@ -0,0 +1,101 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
|
||||
=======
|
||||
>>>>>>> add Comic models
|
||||
|
||||
|
||||
class Publisher(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Artist(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Comic(models.Model):
|
||||
<<<<<<< HEAD
|
||||
title = models.CharField(max_length=60)
|
||||
publisher = models.ForeignKey(Publisher)
|
||||
current_order = models.BooleanField()
|
||||
completed = models.BooleanField()
|
||||
writer = models.ForeignKey(Artist, blank=True, null=True, related_name='writer')
|
||||
artist = models.ForeignKey(Artist, blank=True, null=True, related_name='artist')
|
||||
=======
|
||||
title = models.CharField(max_length=40)
|
||||
publisher = models.ForeignKey(Publisher)
|
||||
current_order = models.BooleanField()
|
||||
completed = models.BooleanField()
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> add Comic models
|
||||
=======
|
||||
writer = models.ForeignKey(Artist, null=True, related_name='writer')
|
||||
artist = models.ForeignKey(Artist, null=True, related_name='artist')
|
||||
>>>>>>> backup
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
|
||||
class Volume(models.Model):
|
||||
name = models.CharField(max_length=40)
|
||||
comic = models.ForeignKey(Comic)
|
||||
|
||||
def __str__(self):
|
||||
return self.comic.title + ' ' + self.name
|
||||
|
||||
|
||||
class Issue(models.Model):
|
||||
number = models.CharField(max_length=4)
|
||||
is_read = models.BooleanField(default=False)
|
||||
in_stock = models.BooleanField(default=False)
|
||||
comic = models.ForeignKey(Comic, null=True)
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
volume = models.ForeignKey(Volume, blank=True, null=True)
|
||||
=======
|
||||
>>>>>>> add Comic models
|
||||
=======
|
||||
volume = models.ForeignKey(Volume, null=True)
|
||||
>>>>>>> backup
|
||||
|
||||
def __str__(self):
|
||||
return self.comic.title + ' #' + self.number
|
||||
|
||||
|
||||
class StoryArc(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
comic = models.ForeignKey(Comic, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class TradePaperback(models.Model):
|
||||
name = models.CharField(max_length=40)
|
||||
comic = models.ForeignKey(Comic, null=True)
|
||||
issue_start = models.IntegerField()
|
||||
issue_end = models.IntegerField()
|
||||
|
||||
def __str__(self):
|
||||
<<<<<<< HEAD
|
||||
return self.comic.title + ' TP ' + self.name
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
>>>>>>> add Comic models
|
||||
=======
|
||||
return self.comic.title + ' TP ' + self.name
|
||||
>>>>>>> backup
|
||||
@@ -0,0 +1,6 @@
|
||||
li a {
|
||||
color: green;
|
||||
}
|
||||
td a {
|
||||
color: green;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<<<<<<< HEAD
|
||||
{% extends "kontor/base.html" %}
|
||||
{% block title %}Comic Details{% endblock %}
|
||||
{% block content %}
|
||||
=======
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Comic Details</title>
|
||||
</head>
|
||||
<body>
|
||||
>>>>>>> backup
|
||||
<table>
|
||||
<thead><td>Name</td><td>current Order</td><td>completed</td></thead>
|
||||
<tr>
|
||||
<td>{{ comic.title }}</td>
|
||||
<td>{{ comic.current_order }}</td>
|
||||
<td>{{ comic.completed }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<<<<<<< HEAD
|
||||
{% endblock %}
|
||||
=======
|
||||
|
||||
</body>
|
||||
</html>
|
||||
>>>>>>> backup
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
<<<<<<< HEAD
|
||||
{% extends "kontor/base.html" %}
|
||||
{% block title %}Comic Overview{% endblock %}
|
||||
{% block content %}
|
||||
=======
|
||||
{% load staticfiles %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'comics/style.css' %}" />
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Comic Overview</title>
|
||||
</head>
|
||||
<body>
|
||||
>>>>>>> backup
|
||||
<table>
|
||||
<thead><td>Name</td><td>current Order</td><td>completed</td></thead>
|
||||
{% for comic in comic_list %}
|
||||
<tr>
|
||||
<td><a href="{% url 'comics:detail' comic.id %}">{{ comic.title }}</a></td>
|
||||
<td>{{ comic.current_order }}</td>
|
||||
<td>{{ comic.completed }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<<<<<<< HEAD
|
||||
{% endblock %}
|
||||
=======
|
||||
</body>
|
||||
</html>
|
||||
>>>>>>> backup
|
||||
@@ -0,0 +1,8 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
class ComicsViewsTestCase(TestCase):
|
||||
def test_index(self):
|
||||
resp = self.client.get('/comics/')
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
@@ -0,0 +1,10 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'comics'
|
||||
urlpatterns = [
|
||||
url(r'^$', views.IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
|
||||
]
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<<<<<<< HEAD
|
||||
from django.shortcuts import render
|
||||
<<<<<<< HEAD
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views import generic
|
||||
|
||||
from .models import Comic
|
||||
# Create your views here.
|
||||
|
||||
|
||||
class IndexView(generic.ListView):
|
||||
template_name = 'comics/index.html'
|
||||
context_object_name = 'comic_list'
|
||||
|
||||
def get_queryset(self):
|
||||
"""Return the list of comics."""
|
||||
return Comic.objects.all()
|
||||
|
||||
|
||||
class DetailView(generic.DetailView):
|
||||
model = Comic
|
||||
template_name = 'comics/detail.html'
|
||||
=======
|
||||
=======
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views import generic
|
||||
>>>>>>> backup
|
||||
|
||||
from .models import Comic
|
||||
# Create your views here.
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
|
||||
|
||||
class IndexView(generic.ListView):
|
||||
template_name = 'comics/index.html'
|
||||
context_object_name = 'comic_list'
|
||||
|
||||
def get_queryset(self):
|
||||
"""Return the list of comics."""
|
||||
return Comic.objects.all()
|
||||
|
||||
|
||||
class DetailView(generic.DetailView):
|
||||
model = Comic
|
||||
template_name = 'comics/detail.html'
|
||||
>>>>>>> backup
|
||||
Executable
Executable
+3
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HomeOfficeConfig(AppConfig):
|
||||
name = 'homeoffice'
|
||||
@@ -0,0 +1,28 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9.6 on 2016-06-20 16:58
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Expense',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Travel',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
],
|
||||
),
|
||||
]
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
class Travel(models.Model):
|
||||
pass
|
||||
|
||||
class Expense(models.Model):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
{% extends "kontor/base.html" %}
|
||||
{% block title %}HomeOffice{% endblock %}
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
from django.conf.urls import url
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'homeoffice'
|
||||
urlpatterns = [
|
||||
url(r'^$', views.index, name='index'),
|
||||
]
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
def index(request):
|
||||
return render(request, 'homeoffice/index.html')
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
Django settings for kontor project.
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
Generated by 'django-admin startproject' using Django 1.9.6.
|
||||
=======
|
||||
Generated by 'django-admin startproject' using Django 1.9.5.
|
||||
>>>>>>> initial setup
|
||||
|
||||
=======
|
||||
>>>>>>> backup
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.6/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/1.6/ref/settings/
|
||||
"""
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
import os
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/1.6/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
SECRET_KEY = '8s-(!r%0yu)b(hdkes_v#g8kp7)0ma851j+&43cds#duk!s-_c'
|
||||
=======
|
||||
SECRET_KEY = 'c)&86w07k)091bgi1llt+aol5$8in8g=n#+iba4784cdw$#h)^'
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
SECRET_KEY = 'i5qx(%mex)4ovh#y4m94b1(3xiw4%8+rx-!kpw9v4q*@0v6pd2'
|
||||
>>>>>>> backup
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
TEMPLATE_DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
<<<<<<< HEAD
|
||||
INSTALLED_APPS = [
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< HEAD
|
||||
'comics.apps.ComicsConfig',
|
||||
'library.apps.LibraryConfig',
|
||||
'tysc.apps.TyscConfig',
|
||||
'tradingcards.apps.TradingCardsConfig',
|
||||
'homeoffice.apps.HomeOfficeConfig',
|
||||
'medien.apps.MedienConfig',
|
||||
=======
|
||||
>>>>>>> initial setup
|
||||
=======
|
||||
'comics.apps.ComicsConfig',
|
||||
>>>>>>> add admin site for Comics
|
||||
=======
|
||||
INSTALLED_APPS = (
|
||||
'comics.apps.ComicsConfig',
|
||||
'tysc.apps.TyscConfig',
|
||||
'library.apps.LibraryConfig',
|
||||
>>>>>>> backup
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'kontor.urls'
|
||||
|
||||
<<<<<<< HEAD
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
<<<<<<< HEAD
|
||||
'DIRS': [os.path.join(BASE_DIR, 'templates')],
|
||||
=======
|
||||
'DIRS': [],
|
||||
>>>>>>> initial setup
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
<<<<<<< HEAD
|
||||
'django.template.context_processors.i18n',
|
||||
'django.template.context_processors.media',
|
||||
'django.template.context_processors.static',
|
||||
'django.template.context_processors.tz',
|
||||
=======
|
||||
>>>>>>> initial setup
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
=======
|
||||
>>>>>>> backup
|
||||
WSGI_APPLICATION = 'kontor.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/1.6/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
# 'ENGINE': 'django.db.backends.dummy',
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
}
|
||||
}
|
||||
|
||||
#from mongoengine import connect
|
||||
#connect('kontor')
|
||||
|
||||
#SESSION_ENGINE = 'mongoengine.django.sessions'
|
||||
#SESSION_SERIALIZER = 'mongoengine.django.sessions.BSONSerializer'
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.6/topics/i18n/
|
||||
|
||||
<<<<<<< HEAD
|
||||
LANGUAGE_CODE = 'de-de'
|
||||
=======
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
>>>>>>> initial setup
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.6/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
<<<<<<< HEAD
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, "static"),
|
||||
]
|
||||
=======
|
||||
>>>>>>> initial setup
|
||||
Executable
+61
@@ -0,0 +1,61 @@
|
||||
from django.conf.urls import patterns, include, url
|
||||
|
||||
<<<<<<< HEAD
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/1.9/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.conf.urls import url, include
|
||||
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
|
||||
"""
|
||||
<<<<<<< HEAD
|
||||
from django.conf.urls import url, include
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth import views as auth_views
|
||||
|
||||
from . import views
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = [
|
||||
#url(r'^admin/', admin.site.urls),
|
||||
url('^', include('django.contrib.auth.urls')),
|
||||
url(r'^$', views.index, name='index'),
|
||||
#url(r'^accounts/login/$', auth_views.login),
|
||||
url(r'^accounts/login/$', auth_views.login, {'template_name': 'kontor/login.html'}),
|
||||
url(r'^accounts/logout/$', auth_views.logout ),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^comics/', include('comics.urls')),
|
||||
url(r'^office/', include('homeoffice.urls')),
|
||||
url(r'^tradingcards/', include('tradingcards.urls')),
|
||||
url(r'^tysc/', include('tysc.urls')),
|
||||
url(r'^library/', include('library.urls')),
|
||||
url(r'^medien/', include('medien.urls')),
|
||||
=======
|
||||
from django.conf.urls import url
|
||||
=======
|
||||
>>>>>>> backup
|
||||
from django.contrib import admin
|
||||
admin.autodiscover()
|
||||
|
||||
<<<<<<< HEAD
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
>>>>>>> initial setup
|
||||
]
|
||||
=======
|
||||
urlpatterns = patterns('',
|
||||
# Examples:
|
||||
#url(r'^$', 'kontor.views.home', name='home'),
|
||||
# url(r'^blog/', include('blog.urls')),
|
||||
url(r'^comics/', include('comics.urls')),
|
||||
url(r'^tysc/', include('tysc.urls')),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
>>>>>>> backup
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
from django.shortcuts import render
|
||||
from django.http import HttpResponse
|
||||
|
||||
|
||||
def index(request):
|
||||
<<<<<<< HEAD
|
||||
return render(request, 'kontor/index.html')
|
||||
=======
|
||||
return HttpResponse('You are in the Kontor application')
|
||||
>>>>>>> backup
|
||||
|
||||
|
||||
def home(request):
|
||||
return HttpResponse('You are in the Kontor application')
|
||||
@@ -0,0 +1,14 @@
|
||||
"""
|
||||
WSGI config for kontor project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/1.6/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "kontor.settings")
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
application = get_wsgi_application()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user