Browse Source

Merge branch 'master' of git.xpub.nl:/var/www/git.xpub.nl/repos/xpub-lib

ansible-setup-and-deploy
Jocavdh 6 years ago
parent
commit
a0558a6115
  1. BIN
      .DS_Store
  2. 1
      .gitignore
  3. 0
      1_Benjamin_Walter_Understanding_Brecht1.txt
  4. 0
      3_Thomas_Piketty_-_Capital_in_the_Twenty-First_Century_2014A.txt
  5. 4
      app/__init__.py
  6. 4
      app/cover.py
  7. BIN
      app/cover/2_Drucker_Johanna_Graphesis_Visual_Forms_of_Knowledge_Production_cover.png
  8. BIN
      app/cover/2_Drucker_Johanna_Graphesis_Visual_Forms_of_Knowledge_Productioncover_small.png
  9. BIN
      app/cover/2_Joseph_Weizenbaum_-_Computer_Power_and_Human_Reason__From_Judgement_to_Calculation_1976_W._H._Freeman__Company_cover.png
  10. BIN
      app/cover/2_Joseph_Weizenbaum_-_Computer_Power_and_Human_Reason__From_Judgement_to_Calculation_1976_W._H._Freeman__Companycover_small.png
  11. 15
      app/forms.py
  12. 4
      app/models.py
  13. 68
      app/static/css/style.css
  14. 25
      app/static/js/app.js
  15. 2
      app/static/js/jquery-3.3.1.min.js
  16. 486
      app/static/js/jquery.marquee.js
  17. 16
      app/static/js/jquery.marquee.min.js
  18. 18
      app/templates/about.html
  19. 9
      app/templates/add_book.html
  20. 18
      app/templates/add_stack.html
  21. 2
      app/templates/add_to_stacks.html
  22. 29
      app/templates/base.html
  23. 67
      app/templates/edit_book_detail.html
  24. 6
      app/templates/header.html
  25. 11
      app/templates/home.html
  26. 19
      app/templates/red_link.html
  27. 23
      app/templates/results.html
  28. 10
      app/templates/results_grid.html
  29. 8
      app/templates/scape.html
  30. 84
      app/templates/show_book_detail.html
  31. 21
      app/templates/show_books.html
  32. 4
      app/templates/show_books_grid.html
  33. 33
      app/templates/show_stack_detail.html
  34. 11
      app/templates/show_stack_detail_tab.html
  35. 156
      app/views.py
  36. 2
      import_csv.py
  37. 2
      init_db.sh
  38. BIN
      whoosh/Book/MAIN_071mbyqoitijmyv4.seg
  39. BIN
      whoosh/Book/MAIN_2a7vygakwdvdefoe.seg
  40. BIN
      whoosh/Book/MAIN_5id3ip2mvmsu2mbl.seg
  41. BIN
      whoosh/Book/MAIN_6bsjkp9xdes7zrrx.seg
  42. 0
      whoosh/Book/MAIN_WRITELOCK
  43. BIN
      whoosh/Book/MAIN_es8t5xyup7nfqm7j.seg
  44. BIN
      whoosh/Book/MAIN_hljrpqpa46dk3vgc.seg
  45. BIN
      whoosh/Book/MAIN_p6r8oedtat7ay25v.seg
  46. BIN
      whoosh/Book/MAIN_q0ohjvxyt5ufv1vl.seg
  47. BIN
      whoosh/Book/_MAIN_293.toc

BIN
.DS_Store

Binary file not shown.

1
.gitignore

@ -3,6 +3,7 @@ __pycache__/
*~
app/uploads/**
!app/uploads/cover
/cover/**
app/mydatabase.db
pyrqlite/
whoosh/

0
1_Benjamin_Walter_Understanding_Brecht1.txt

0
3_Thomas_Piketty_-_Capital_in_the_Twenty-First_Century_2014A.txt

4
app/__init__.py

@ -15,7 +15,7 @@ registry.register("rqlite.pyrqlite", "sqlalchemy_rqlite.pyrqlite", "dialect")
basedir = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = os.path.join(basedir, 'uploads')
UPLOAD_FOLDER_COVER = os.path.join(basedir, 'uploads/cover')
UPLOAD_FOLDER_COVER = os.path.join(basedir, 'cover')
#ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
load_dotenv(find_dotenv())
app = Flask(__name__)
@ -29,7 +29,7 @@ app.config['PORT'] = 80
#app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'mydatabase.db')
db = SQLAlchemy(app)
light = not os.path.isdir(UPLOAD_FOLDER)
DOMAIN = environ.get('DOMAIN')
socketio = SocketIO(app)

4
app/cover.py

@ -37,8 +37,8 @@ def get_cover(file_path, filename):
# Convert each page to a png image.
for page in pages:
big_filename = "app/uploads/cover/"+page["filename"] + "_cover.png"
small_filename = "app/uploads/cover/"+page["filename"] + "cover_small" + ".png"
big_filename = "app/cover/"+page["filename"] + "_cover.png"
small_filename = "app/cover/"+page["filename"] + "cover_small" + ".png"
img = pdf_page_to_png(src_pdf, pagenum = page["pagenum"], resolution = 200)
img.save(filename = big_filename)

BIN
app/cover/2_Drucker_Johanna_Graphesis_Visual_Forms_of_Knowledge_Production_cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
app/cover/2_Drucker_Johanna_Graphesis_Visual_Forms_of_Knowledge_Productioncover_small.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
app/cover/2_Joseph_Weizenbaum_-_Computer_Power_and_Human_Reason__From_Judgement_to_Calculation_1976_W._H._Freeman__Company_cover.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
app/cover/2_Joseph_Weizenbaum_-_Computer_Power_and_Human_Reason__From_Judgement_to_Calculation_1976_W._H._Freeman__Companycover_small.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

15
app/forms.py

@ -25,7 +25,6 @@ class UploadForm(FlaskForm):
sameness = DecimalRangeField('sameness', default=0)
diversity = DecimalRangeField('diversity', default=0)
gender = DecimalRangeField('gender', default=50)
time = StringField('time', [validators.Length(max=5)],default=None)
choices = [('Student', 'Student'),
('Librarian', 'Librarian'),
('Pirate', 'Pirate'),
@ -43,6 +42,17 @@ class EditForm(FlaskForm):
year_published = StringField('year published', [validators.Length(max=4)],default=None)
file = FileField()
message = StringField('message')
sameness = DecimalRangeField('sameness', default=0)
diversity = DecimalRangeField('diversity', default=0)
gender = DecimalRangeField('gender', default=50)
choices = [('Student', 'Student'),
('Librarian', 'Librarian'),
('Pirate', 'Pirate'),
('Teacher', 'Teacher'),
('Institution', 'Institution'),
('All of the above', 'All of the above'),
('None of the above', 'None of the above')]
who = SelectField('', choices=choices, default='Student')
class ChatForm(FlaskForm):
message = StringField('message', validators=[InputRequired()])
@ -66,7 +76,8 @@ class SearchForm(FlaskForm):
('Title', 'Title'),
('Author', 'Author'),
('Category', 'Category'),
('Stack', 'Stack')]
('Stack', 'Stack'),
('Outliers', 'Outliers')]
select = SelectField('', choices=choices, default='All')
search = StringField('', validators=[InputRequired()])
grid = SubmitField('Grid')

4
app/models.py

@ -46,10 +46,9 @@ class Book(db.Model):
diversity = db.Column(db.Numeric())
gender = db.Column(db.Numeric())
who = db.Column(db.String(255))
time = db.Column(db.Numeric())
def __init__(self, title, file, cover, fileformat, category, year_published, message, sameness, diversity, gender, who, time):
def __init__(self, title, file, cover, fileformat, category, year_published, message, sameness, diversity, gender, who):
self.title = title
self.file = file
self.cover = cover
@ -64,7 +63,6 @@ class Book(db.Model):
self.diversity = diversity
self.gender = gender
self.who = who
self.time = time
def __repr__(self):

68
app/static/css/style.css

@ -5,14 +5,14 @@ font-family: "Archivo Narrow";
}
p{
font-size: 18px;
font-size: 20px;
}
a{
text-decoration: none;
text-decoration: underline;
color: black;
}
a:hover{
@ -68,7 +68,7 @@ padding: 0px 10px;
}
.about{
font-size: 16px;
font-size: 18px;
}
.library_table{
@ -106,7 +106,7 @@ background-color: #E8E8E8!important;
}
.library_table .author_col{
font-size: 15px;
font-size: 18px;
}
.library_table li{
@ -121,6 +121,10 @@ display: inline-block;
font-size: 10px;
}
#ascii {
text-align: center;
}
.library_table tr:nth-child(even){
background-color: #fafafa;
@ -129,7 +133,6 @@ background-color: #fafafa;
#title_xppl{
font-size: 46px;
cursor: pointer;
}
.header input{
@ -142,7 +145,7 @@ font-weight: bold;
.author input{
height:20px;
width: 500px;
font-size: 16px;
font-size: 18px;
}
.search input{
@ -242,7 +245,7 @@ div.marquee > div.marquee-text {
position: fixed;
bottom: 0;
right: 0;
width: 30%;
width: 25%;
height: 100%;
}
@ -254,7 +257,7 @@ div.marquee > div.marquee-text {
padding: 10px;
margin: 0px;
height: 100%;
background-color: #b4b9be;
/*background-color: #b4b9be;*/
overflow-y: scroll;
overflow-x: hidden;
color: white;
@ -265,6 +268,43 @@ div.marquee > div.marquee-text {
z-index: -100000;
}
.messageback1{
position: absolute;
bottom: 40px;
display: block;
width:100%;
padding: 0px;
margin: 0px;
height: 100%;
background-color: #b4b9be;
overflow-y: scroll;
overflow-x: hidden;
color: white;
word-wrap:break-word;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
z-index: -100000;}
.messageback2{
position: absolute;
bottom: 40px;
display: block;
width:100%;
padding: 0px;
margin: 0px;
height: 100%;
background-color: #b4b9be;
overflow-y: scroll;
overflow-x: hidden;
color: white;
word-wrap:break-word;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
z-index: -100000;}
.new-message {
position: absolute;
bottom: 0;
@ -273,6 +313,7 @@ width:100%;
margin:0;
padding:0;
z-index: 100000;
opacity: 1!important;
}
.control{
display: block;
@ -353,7 +394,7 @@ box-sizing: border-box;
justify-items: center;
}
@media screen and (max-width: 900px) {
@media screen and (max-width: 1000px) {
.grid{
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
@ -362,11 +403,13 @@ box-sizing: border-box;
justify-items: center;
}
}
@media screen and (max-width: 400px) {
@media screen and (max-width: 600px) {
.grid{
display: grid;
grid-template-columns: 1fr;
align-items: center;
grid-template-columns: 1fr 1fr 1fr;
grid-gap: 2px;
align-items: top;
justify-items: center;
}
}
@ -377,6 +420,7 @@ box-sizing: border-box;
align-items: center;
justify-items: center;
}
.gridbox:hover{
opacity: 0.5;
}

25
app/static/js/app.js

@ -63,13 +63,34 @@ function generateTitle(elem) {
}
$(function() {
$("#tabs").tabs().addClass("ui-tabs-vertical ui-helper-clearfix");
var index = 'ui-tabs-active';
// Define friendly data store name
var dataStore = window.sessionStorage;
var oldIndex = 0;
// Start magic!
try {
// getter: Fetch previous value
oldIndex = dataStore.getItem(index);
} catch(e) {}
$("#tabs").tabs({
active: oldIndex,
activate: function(event, ui) {
// Get future value
var newIndex = ui.newTab.parent().children().index(ui.newTab);
// Set future value
try {
dataStore.setItem( index, newIndex );
} catch(e) {}
}
});
$("#tabs").addClass("ui-tabs-vertical ui-helper-clearfix");
$("#tabs li").removeClass("ui-corner-top").addClass("ui-corner-left");
});
$(".no_cover").each(function() {
var string = $(this).attr('id')
var randomColor = colorHash(string).rgb

2
app/static/js/jquery-3.3.1.min.js

File diff suppressed because one or more lines are too long

486
app/static/js/jquery.marquee.js

@ -0,0 +1,486 @@
/**
* jQuery.marquee - scrolling text like old marquee element
* @author Aamir Afridi - aamirafridi(at)gmail(dot)com / http://aamirafridi.com/jquery/jquery-marquee-plugin
*/;
(function($) {
$.fn.marquee = function(options) {
return this.each(function() {
// Extend the options if any provided
var o = $.extend({}, $.fn.marquee.defaults, options),
$this = $(this),
$marqueeWrapper, containerWidth, animationCss, verticalDir, elWidth,
loopCount = 3,
playState = 'animation-play-state',
css3AnimationIsSupported = false,
// Private methods
_prefixedEvent = function(element, type, callback) {
var pfx = ["webkit", "moz", "MS", "o", ""];
for (var p = 0; p < pfx.length; p++) {
if (!pfx[p]) type = type.toLowerCase();
element.addEventListener(pfx[p] + type, callback, false);
}
},
_objToString = function(obj) {
var tabjson = [];
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
tabjson.push(p + ':' + obj[p]);
}
}
tabjson.push();
return '{' + tabjson.join(',') + '}';
},
_startAnimationWithDelay = function() {
$this.timer = setTimeout(animate, o.delayBeforeStart);
},
// Public methods
methods = {
pause: function() {
if (css3AnimationIsSupported && o.allowCss3Support) {
$marqueeWrapper.css(playState, 'paused');
} else {
// pause using pause plugin
if ($.fn.pause) {
$marqueeWrapper.pause();
}
}
// save the status
$this.data('runningStatus', 'paused');
// fire event
$this.trigger('paused');
},
resume: function() {
// resume using css3
if (css3AnimationIsSupported && o.allowCss3Support) {
$marqueeWrapper.css(playState, 'running');
} else {
// resume using pause plugin
if ($.fn.resume) {
$marqueeWrapper.resume();
}
}
// save the status
$this.data('runningStatus', 'resumed');
// fire event
$this.trigger('resumed');
},
toggle: function() {
methods[$this.data('runningStatus') == 'resumed' ? 'pause' : 'resume']();
},
destroy: function() {
// Clear timer
clearTimeout($this.timer);
// Unbind all events
$this.find("*").addBack().unbind();
// Just unwrap the elements that has been added using this plugin
$this.html($this.find('.js-marquee:first').html());
}
};
// Check for methods
if (typeof options === 'string') {
if ($.isFunction(methods[options])) {
// Following two IF statements to support public methods
if (!$marqueeWrapper) {
$marqueeWrapper = $this.find('.js-marquee-wrapper');
}
if ($this.data('css3AnimationIsSupported') === true) {
css3AnimationIsSupported = true;
}
methods[options]();
}
return;
}
/* Check if element has data attributes. They have top priority
For details https://twitter.com/aamirafridi/status/403848044069679104 - Can't find a better solution :/
jQuery 1.3.2 doesn't support $.data().KEY hence writting the following */
var dataAttributes = {},
attr;
$.each(o, function(key, value) {
// Check if element has this data attribute
attr = $this.attr('data-' + key);
if (typeof attr !== 'undefined') {
// Now check if value is boolean or not
switch (attr) {
case 'true':
attr = true;
break;
case 'false':
attr = false;
break;
}
o[key] = attr;
}
});
// Reintroduce speed as an option. It calculates duration as a factor of the container width
// measured in pixels per second.
if (o.speed) {
o.duration = parseInt($this.width(), 10) / o.speed * 1000;
}
// Shortcut to see if direction is upward or downward
verticalDir = o.direction == 'up' || o.direction == 'down';
// no gap if not duplicated
o.gap = o.duplicated ? parseInt(o.gap) : 0;
// wrap inner content into a div
$this.wrapInner('<div class="js-marquee"></div>');
// Make copy of the element
var $el = $this.find('.js-marquee').css({
'margin-right': o.gap,
'float': 'left'
});
if (o.duplicated) {
$el.clone(true).appendTo($this);
}
// wrap both inner elements into one div
$this.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>');
// Save the reference of the wrapper
$marqueeWrapper = $this.find('.js-marquee-wrapper');
// If direction is up or down, get the height of main element
if (verticalDir) {
var containerHeight = $this.height();
$marqueeWrapper.removeAttr('style');
$this.height(containerHeight);
// Change the CSS for js-marquee element
$this.find('.js-marquee').css({
'float': 'none',
'margin-bottom': o.gap,
'margin-right': 0
});
// Remove bottom margin from 2nd element if duplicated
if (o.duplicated) $this.find('.js-marquee:last').css({
'margin-bottom': 0
});
var elHeight = $this.find('.js-marquee:first').height() + o.gap;
// adjust the animation duration according to the text length
if (o.startVisible && !o.duplicated) {
// Compute the complete animation duration and save it for later reference
// formula is to: (Height of the text node + height of the main container / Height of the main container) * duration;
o._completeDuration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration;
// formula is to: (Height of the text node / height of the main container) * duration
o.duration = (parseInt(elHeight, 10) / parseInt(containerHeight, 10)) * o.duration;
} else {
// formula is to: (Height of the text node + height of the main container / Height of the main container) * duration;
o.duration = ((parseInt(elHeight, 10) + parseInt(containerHeight, 10)) / parseInt(containerHeight, 10)) * o.duration;
}
} else {
// Save the width of the each element so we can use it in animation
elWidth = $this.find('.js-marquee:first').width() + o.gap;
// container width
containerWidth = $this.width();
// adjust the animation duration according to the text length
if (o.startVisible && !o.duplicated) {
// Compute the complete animation duration and save it for later reference
// formula is to: (Width of the text node + width of the main container / Width of the main container) * duration;
o._completeDuration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration;
// (Width of the text node / width of the main container) * duration
o.duration = (parseInt(elWidth, 10) / parseInt(containerWidth, 10)) * o.duration;
} else {
// formula is to: (Width of the text node + width of the main container / Width of the main container) * duration;
o.duration = ((parseInt(elWidth, 10) + parseInt(containerWidth, 10)) / parseInt(containerWidth, 10)) * o.duration;
}
}
// if duplicated then reduce the duration
if (o.duplicated) {
o.duration = o.duration / 2;
}
if (o.allowCss3Support) {
var
elm = document.body || document.createElement('div'),
animationName = 'marqueeAnimation-' + Math.floor(Math.random() * 10000000),
domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
animationString = 'animation',
animationCss3Str = '',
keyframeString = '';
// Check css3 support
if (elm.style.animation !== undefined) {
keyframeString = '@keyframes ' + animationName + ' ';
css3AnimationIsSupported = true;
}
if (css3AnimationIsSupported === false) {
for (var i = 0; i < domPrefixes.length; i++) {
if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
var prefix = '-' + domPrefixes[i].toLowerCase() + '-';
animationString = prefix + animationString;
playState = prefix + playState;
keyframeString = '@' + prefix + 'keyframes ' + animationName + ' ';
css3AnimationIsSupported = true;
break;
}
}
}
if (css3AnimationIsSupported) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's infinite ' + o.css3easing;
$this.data('css3AnimationIsSupported', true);
}
}
var _rePositionVertically = function() {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? containerHeight + 'px' : '-' + elHeight + 'px') + ')');
},
_rePositionHorizontally = function() {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? containerWidth + 'px' : '-' + elWidth + 'px') + ')');
};
// if duplicated option is set to true than position the wrapper
if (o.duplicated) {
if (verticalDir) {
if (o.startVisible) {
$marqueeWrapper.css('transform', 'translateY(0)');
} else {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? containerHeight + 'px' : '-' + ((elHeight * 2) - o.gap) + 'px') + ')');
}
} else {
if (o.startVisible) {
$marqueeWrapper.css('transform', 'translateX(0)');
} else {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? containerWidth + 'px' : '-' + ((elWidth * 2) - o.gap) + 'px') + ')');
}
}
// If the text starts out visible we can skip the two initial loops
if (!o.startVisible) {
loopCount = 1;
}
} else if (o.startVisible) {
// We only have two different loops if marquee is duplicated and starts visible
loopCount = 2;
} else {
if (verticalDir) {
_rePositionVertically();
} else {
_rePositionHorizontally();
}
}
// Animate recursive method
var animate = function() {
if (o.duplicated) {
// When duplicated, the first loop will be scroll longer so double the duration
if (loopCount === 1) {
o._originalDuration = o.duration;
if (verticalDir) {
o.duration = o.direction == 'up' ? o.duration + (containerHeight / ((elHeight) / o.duration)) : o.duration * 2;
} else {
o.duration = o.direction == 'left' ? o.duration + (containerWidth / ((elWidth) / o.duration)) : o.duration * 2;
}
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
loopCount++;
}
// On 2nd loop things back to normal, normal duration for the rest of animations
else if (loopCount === 2) {
o.duration = o._originalDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
loopCount++;
}
}
if (verticalDir) {
if (o.duplicated) {
// Adjust the starting point of animation only when first loops finishes
if (loopCount > 2) {
$marqueeWrapper.css('transform', 'translateY(' + (o.direction == 'up' ? 0 : '-' + elHeight + 'px') + ')');
}
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + elHeight + 'px' : 0) + ')'
};
} else if (o.startVisible) {
// This loop moves the marquee out of the container
if (loopCount === 2) {
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + elHeight + 'px' : containerHeight + 'px') + ')'
};
loopCount++;
} else if (loopCount === 3) {
// Set the duration for the animation that will run forever
o.duration = o._completeDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
_rePositionVertically();
}
} else {
_rePositionVertically();
animationCss = {
'transform': 'translateY(' + (o.direction == 'up' ? '-' + ($marqueeWrapper.height()) + 'px' : containerHeight + 'px') + ')'
};
}
} else {
if (o.duplicated) {
// Adjust the starting point of animation only when first loops finishes
if (loopCount > 2) {
$marqueeWrapper.css('transform', 'translateX(' + (o.direction == 'left' ? 0 : '-' + elWidth + 'px') + ')');
}
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : 0) + ')'
};
} else if (o.startVisible) {
// This loop moves the marquee out of the container
if (loopCount === 2) {
// Adjust the css3 animation as well
if (animationCss3Str) {
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's ' + o.delayBeforeStart / 1000 + 's ' + o.css3easing;
}
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')'
};
loopCount++;
} else if (loopCount === 3) {
// Set the duration for the animation that will run forever
o.duration = o._completeDuration;
// Adjust the css3 animation as well
if (animationCss3Str) {
animationName = animationName + '0';
keyframeString = $.trim(keyframeString) + '0 ';
animationCss3Str = animationName + ' ' + o.duration / 1000 + 's 0s infinite ' + o.css3easing;
}
_rePositionHorizontally();
}
} else {
_rePositionHorizontally();
animationCss = {
'transform': 'translateX(' + (o.direction == 'left' ? '-' + elWidth + 'px' : containerWidth + 'px') + ')'
};
}
}
// fire event
$this.trigger('beforeStarting');
// If css3 support is available than do it with css3, otherwise use jQuery as fallback
if (css3AnimationIsSupported) {
// Add css3 animation to the element
$marqueeWrapper.css(animationString, animationCss3Str);
var keyframeCss = keyframeString + ' { 100% ' + _objToString(animationCss) + '}',
$styles = $marqueeWrapper.find('style');
// Now add the keyframe animation to the marquee element
if ($styles.length !== 0) {
// Bug fixed for jQuery 1.3.x - Instead of using .last(), use following
$styles.filter(":last").html(keyframeCss);
} else {
$('head').append('<style>' + keyframeCss + '</style>');
}
// Animation iteration event
_prefixedEvent($marqueeWrapper[0], "AnimationIteration", function() {
$this.trigger('finished');
});
// Animation stopped
_prefixedEvent($marqueeWrapper[0], "AnimationEnd", function() {
animate();
$this.trigger('finished');
});
} else {
// Start animating
$marqueeWrapper.animate(animationCss, o.duration, o.easing, function() {
// fire event
$this.trigger('finished');
// animate again
if (o.pauseOnCycle) {
_startAnimationWithDelay();
} else {
animate();
}
});
}
// save the status
$this.data('runningStatus', 'resumed');
};
// bind pause and resume events
$this.bind('pause', methods.pause);
$this.bind('resume', methods.resume);
if (o.pauseOnHover) {
$this.bind('mouseenter', methods.pause);
$this.bind('mouseleave', methods.resume);
}
// If css3 animation is supported than call animate method at once
if (css3AnimationIsSupported && o.allowCss3Support) {
animate();
} else {
// Starts the recursive method
_startAnimationWithDelay();
}
});
}; // End of Plugin
// Public: plugin defaults options
$.fn.marquee.defaults = {
// If you wish to always animate using jQuery
allowCss3Support: true,
// works when allowCss3Support is set to true - for full list see http://www.w3.org/TR/2013/WD-css3-transitions-20131119/#transition-timing-function
css3easing: 'linear',
// requires jQuery easing plugin. Default is 'linear'
easing: 'linear',
// pause time before the next animation turn in milliseconds
delayBeforeStart: 1000,
// 'left', 'right', 'up' or 'down'
direction: 'left',
// true or false - should the marquee be duplicated to show an effect of continues flow
duplicated: false,
// duration in milliseconds of the marquee in milliseconds
duration: 5000,
// gap in pixels between the tickers
gap: 20,
// on cycle pause the marquee
pauseOnCycle: false,
// on hover pause the marquee - using jQuery plugin https://github.com/tobia/Pause
pauseOnHover: false,
// the marquee is visible initially positioned next to the border towards it will be moving
startVisible: false
};
})(jQuery);

16
app/static/js/jquery.marquee.min.js

@ -0,0 +1,16 @@
/**
* jQuery.marquee - scrolling text like old marquee element
* @author Aamir Afridi - aamirafridi(at)gmail(dot)com / http://aamirafridi.com/jquery/jquery-marquee-plugin
*/
(function(f){f.fn.marquee=function(x){return this.each(function(){var a=f.extend({},f.fn.marquee.defaults,x),b=f(this),c,t,e=3,y="animation-play-state",p=!1,E=function(a,b,c){for(var e=["webkit","moz","MS","o",""],d=0;d<e.length;d++)e[d]||(b=b.toLowerCase()),a.addEventListener(e[d]+b,c,!1)},F=function(a){var b=[],c;for(c in a)a.hasOwnProperty(c)&&b.push(c+":"+a[c]);b.push();return"{"+b.join(",")+"}"},l={pause:function(){p&&a.allowCss3Support?c.css(y,"paused"):f.fn.pause&&c.pause();b.data("runningStatus",
"paused");b.trigger("paused")},resume:function(){p&&a.allowCss3Support?c.css(y,"running"):f.fn.resume&&c.resume();b.data("runningStatus","resumed");b.trigger("resumed")},toggle:function(){l["resumed"==b.data("runningStatus")?"pause":"resume"]()},destroy:function(){clearTimeout(b.timer);b.find("*").addBack().unbind();b.html(b.find(".js-marquee:first").html())}};if("string"===typeof x)f.isFunction(l[x])&&(c||(c=b.find(".js-marquee-wrapper")),!0===b.data("css3AnimationIsSupported")&&(p=!0),l[x]());else{var u;
f.each(a,function(c,d){u=b.attr("data-"+c);if("undefined"!==typeof u){switch(u){case "true":u=!0;break;case "false":u=!1}a[c]=u}});a.speed&&(a.duration=parseInt(b.width(),10)/a.speed*1E3);var v="up"==a.direction||"down"==a.direction;a.gap=a.duplicated?parseInt(a.gap):0;b.wrapInner('<div class="js-marquee"></div>');var h=b.find(".js-marquee").css({"margin-right":a.gap,"float":"left"});a.duplicated&&h.clone(!0).appendTo(b);b.wrapInner('<div style="width:100000px" class="js-marquee-wrapper"></div>');
c=b.find(".js-marquee-wrapper");if(v){var k=b.height();c.removeAttr("style");b.height(k);b.find(".js-marquee").css({"float":"none","margin-bottom":a.gap,"margin-right":0});a.duplicated&&b.find(".js-marquee:last").css({"margin-bottom":0});var q=b.find(".js-marquee:first").height()+a.gap;a.startVisible&&!a.duplicated?(a._completeDuration=(parseInt(q,10)+parseInt(k,10))/parseInt(k,10)*a.duration,a.duration*=parseInt(q,10)/parseInt(k,10)):a.duration*=(parseInt(q,10)+parseInt(k,10))/parseInt(k,10)}else{var m=
b.find(".js-marquee:first").width()+a.gap;var n=b.width();a.startVisible&&!a.duplicated?(a._completeDuration=(parseInt(m,10)+parseInt(n,10))/parseInt(n,10)*a.duration,a.duration*=parseInt(m,10)/parseInt(n,10)):a.duration*=(parseInt(m,10)+parseInt(n,10))/parseInt(n,10)}a.duplicated&&(a.duration/=2);if(a.allowCss3Support){h=document.body||document.createElement("div");var g="marqueeAnimation-"+Math.floor(1E7*Math.random()),A=["Webkit","Moz","O","ms","Khtml"],B="animation",d="",r="";h.style.animation&&
(r="@keyframes "+g+" ",p=!0);if(!1===p)for(var z=0;z<A.length;z++)if(void 0!==h.style[A[z]+"AnimationName"]){h="-"+A[z].toLowerCase()+"-";B=h+B;y=h+y;r="@"+h+"keyframes "+g+" ";p=!0;break}p&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s infinite "+a.css3easing,b.data("css3AnimationIsSupported",!0))}var C=function(){c.css("transform","translateY("+("up"==a.direction?k+"px":"-"+q+"px")+")")},D=function(){c.css("transform","translateX("+("left"==a.direction?n+"px":"-"+m+"px")+")")};a.duplicated?
(v?a.startVisible?c.css("transform","translateY(0)"):c.css("transform","translateY("+("up"==a.direction?k+"px":"-"+(2*q-a.gap)+"px")+")"):a.startVisible?c.css("transform","translateX(0)"):c.css("transform","translateX("+("left"==a.direction?n+"px":"-"+(2*m-a.gap)+"px")+")"),a.startVisible||(e=1)):a.startVisible?e=2:v?C():D();var w=function(){a.duplicated&&(1===e?(a._originalDuration=a.duration,a.duration=v?"up"==a.direction?a.duration+k/(q/a.duration):2*a.duration:"left"==a.direction?a.duration+n/
(m/a.duration):2*a.duration,d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),e++):2===e&&(a.duration=a._originalDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),e++));v?a.duplicated?(2<e&&c.css("transform","translateY("+("up"==a.direction?0:"-"+q+"px")+")"),t={transform:"translateY("+("up"==a.direction?"-"+q+"px":0)+")"}):a.startVisible?2===e?(d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),t={transform:"translateY("+
("up"==a.direction?"-"+q+"px":k+"px")+")"},e++):3===e&&(a.duration=a._completeDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),C()):(C(),t={transform:"translateY("+("up"==a.direction?"-"+c.height()+"px":k+"px")+")"}):a.duplicated?(2<e&&c.css("transform","translateX("+("left"==a.direction?0:"-"+m+"px")+")"),t={transform:"translateX("+("left"==a.direction?"-"+m+"px":0)+")"}):a.startVisible?2===e?(d&&(d=g+" "+a.duration/1E3+"s "+a.delayBeforeStart/1E3+"s "+a.css3easing),
t={transform:"translateX("+("left"==a.direction?"-"+m+"px":n+"px")+")"},e++):3===e&&(a.duration=a._completeDuration,d&&(g+="0",r=f.trim(r)+"0 ",d=g+" "+a.duration/1E3+"s 0s infinite "+a.css3easing),D()):(D(),t={transform:"translateX("+("left"==a.direction?"-"+m+"px":n+"px")+")"});b.trigger("beforeStarting");if(p){c.css(B,d);var h=r+" { 100% "+F(t)+"}",l=c.find("style");0!==l.length?l.filter(":last").html(h):f("head").append("<style>"+h+"</style>");E(c[0],"AnimationIteration",function(){b.trigger("finished")});
E(c[0],"AnimationEnd",function(){w();b.trigger("finished")})}else c.animate(t,a.duration,a.easing,function(){b.trigger("finished");a.pauseOnCycle?b.timer=setTimeout(w,a.delayBeforeStart):w()});b.data("runningStatus","resumed")};b.bind("pause",l.pause);b.bind("resume",l.resume);a.pauseOnHover&&(b.bind("mouseenter",l.pause),b.bind("mouseleave",l.resume));p&&a.allowCss3Support?w():b.timer=setTimeout(w,a.delayBeforeStart)}})};f.fn.marquee.defaults={allowCss3Support:!0,css3easing:"linear",easing:"linear",
delayBeforeStart:1E3,direction:"left",duplicated:!1,duration:5E3,gap:20,pauseOnCycle:!1,pauseOnHover:!1,startVisible:!1}})(jQuery);

18
app/templates/about.html

@ -2,29 +2,27 @@
{% block main %}
<h1 class="page-header">About</h1>
<div style="width: 900px;">
<p class='about'>
XPPL is a project aimed at people who are studying the field of media culture, or as we like to call them: knowledge comrades.
<br>
<br>
This digital library gathers all the books and articles floating around on the shelves of the Piet Zwart Institute, and our hard drives and memory sticks, so that they can be shared, annotated and grouped together into stacks...
This digital library gathers all the books and articles floating around on the shelves of the Piet Zwart Institute, and our hard drives and memory sticks, so that they can be shared, annotated and grouped together into stacks... Its web interface hosts a curated catalogue of books and articles, and its distributed architecture provides instances for uploading and downloading.
<br>
<br>
Its web interface hosts a curated catalogue of books and articles, and its distributed architecture provides instances for uploading and downloading.
It starts at XPUB, but can go anywhere we want it to.
<br>
<br>
It starts at XPUB, but can go anywhere we want it to.</p>
Are you interested in how this library works? Have a look at the source code in <a href 'https://git.xpub.nl/xpub-lib/log.html'> our git. </a>
</p>
<h2> What's the deal with the stacks? </h2>
<p class='about'>
A stack is a number of books that are read at a certain point in time, alternating between them. They usually have a topic in common, or follow a certain study path that can bring you to a point of knowledge. Rather than a bookshelf, where books are lined up and often forgotten, the stack on your table/nightstand/toilet consists of books prone to be opened and reopened at any time.
</p>
<h2>Who's behind this?</h2>
<p class='about'>
We're Angeliki, Alex, Alice, Joca, Natasha and Zalán, helped and supported by Femke, Aymeric, Michael, Steve, Andre, Leslie and many more.
<br>
<br>
XPUB is a study path within the Piet Zwart Institute masters program.
We're Angeliki, Alex, Alice, Joca, Natasha and Zalán, helped and supported by Femke, Aymeric, Michael, Steve, Andre, Leslie and many more. XPUB is a study path within the Piet Zwart Institute masters program.
</p>
</div>
{% endblock %}

9
app/templates/add_book.html

@ -36,7 +36,7 @@ function outputUpdate3(gender) {
<div class="form-group">Title:* <br> {{ form.title (size=50, class="form-control") }}</div>
<br>
<div data-toggle="fieldset" id="phone-fieldset">
Author(s):* <button type="button" data-toggle="fieldset-add-row data-target="#phone-fieldset">+</button>
Author(s):* <button type="button" data-toggle="fieldset-add-row" data-target="#phone-fieldset">+</button>
<table>
<tr>
<th></th>
@ -68,17 +68,12 @@ Check the bibliography. How diverse are the references in this book? <br>
<br><hr align="left" style="width:96%;"><br>
Check the writing. Who is speaking? Is the voice more often male or female? <br>
{{ form.diversity(min=1, max=100, oninput="outputUpdate3(value)") }} &nbsp;
{{ form.gender(min=1, max=100, oninput="outputUpdate3(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="diversity" id="selected-gender">{{ form.gender.data }} </output> % female</span>
<br><hr align="left" style="width:96%;"><br>
Who are you? {{ render_field(form.who) }}
<br><hr align="left" style="width:96%;"><br>
How much time have you spent with this item?
Include the time spent looking for it, and uploading it.<br>
{{ form.time (size = 50, class="form-control")}} <span style="color: #d3d3d3;">hours</span>
<br>
<br><hr align="left" style="width:96%;"><br>
<div style="width: 40%;">

18
app/templates/add_stack.html

@ -22,12 +22,26 @@
{{form.hidden_tag()}}
<br>
{{ render_field(form.stack_name)}}
{{ render_field(form.stack_description)}}
<div style="width: 40%;">
Add a nice description: {{ form.stack_description(size=90, class="form-control") }}
</div>
{{ render_field(form.stack_author)}}
<br>
<button type="submit" class='button'>Create</button>
</form>
<br>
<hr>
<br>
<h1> Stacks currently in the library </h1>
<ul>
{% for stack in stacks %}
<li> <a href="stacks/tab/{{ stack.id }}">
</form>
{{ stack.stack_name }}
</a></td>
{% endfor %}
</ul>

2
app/templates/add_to_stacks.html

@ -6,7 +6,7 @@
<div> Chosen book:
<h1 class="header">{{ book.title }}</h1>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
<img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="150" onerror="if (this.src != '../static/img/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
</div>
<p>These are all the stacks that have been built so far.</p>

29
app/templates/base.html

@ -43,15 +43,22 @@
{% block js %} {% endblock%}
<<<<<<< HEAD
<script type="text/javascript" src="http://code.jquery.com/jquery-2.2.4.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.12.0/jquery-ui.js"></script>
<script type='text/javascript' src='//cdn.jsdelivr.net/jquery.marquee/1.4.0/jquery.marquee.min.js'></script>
=======
<script src="{{ url_for("static", filename="js/jquery-3.3.1.min.js") }}"></script>
<script src="{{ url_for("static", filename="js/jquery-ui-1.12.1.custom/jquery-ui.js") }}"></script>
<script src="{{ url_for("static", filename="js/jquery.marquee.min.js") }}"></script>
>>>>>>> 0f5e5606ab9152e9d27e7081e251e12a7f33824b
<script type="text/javascript" src="{{ url_for("static", filename="js/jquery.tablesorter.js") }}"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script>
<script src="{{ url_for("static", filename="js/vendor/socket.io.slim.js") }}"></script>
<script src="{{ url_for("static", filename="js/vendor/vue.min.js") }}"></script>
<script>
function convertTime(inTime){
var time = inTime;
time = time.match(/(\d+)\-(\d+)\-(\d+)\s*(\d+):(\d+):(\d+)/);
@ -98,6 +105,28 @@ socket.on('connect', function() {
});
socket.on('channel-' + app.channel, function(msg) {
console.log("new: "+msg.text)
$(".messageback1").each(function() {
var oldColor = $(this).css("background-color");
var randomColor = colorHash(msg.text).rgb;
console.log("old: "+oldColor)
console.log("new: "+randomColor)
$(this).css({
background: "-webkit-gradient(linear, left top, left bottom, from("+oldColor+"), to("+randomColor+"))",
backgroundColor: randomColor
});
$('.messageback2').animate({
opacity: 0
}, 1000, function() {
$('.messageback2').css({background: "-webkit-gradient(linear, left top, left bottom, from("+oldColor+"), to("+randomColor+"))", opacity: 1})
});
});
// Add new message to HTML
let my_messages = app.messages;
my_messages.push({

67
app/templates/edit_book_detail.html

@ -1,22 +1,36 @@
{% extends 'base.html' %}
{% block main %}
{% from "_formhelpers.html" import render_field %}
<head>
<script>
function outputUpdate(sameness) {
document.querySelector('#selected-sameness').value = sameness;
}
function outputUpdate2(diversity) {
document.querySelector('#selected-diversity').value = diversity;
}
function outputUpdate3(gender) {
document.querySelector('#selected-gender').value = gender;
}
</script>
</head>
<div class="container">
<a href="{{ url_for('show_book_by_id', id=book.id )}}">back</a>
<form method="POST" action="{{ url_for('edit_book_by_id', id=book.id )}}" enctype=multipart/form-data>
{{ form.csrf_token }}
<div class="form-group"><h1 class="header">{{ form.title.label }} {{ form.title(size=20, class="form-control") }}</h1></div>
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '/uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<div style="float:right; padding-right: 300px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="280px" onerror="if (this.src != '/uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></div>
<br> <br>
<br>
<div data-toggle="fieldset" id="phone-fieldset">
{{ form.author.label }} <button type="button" data-toggle="fieldset-add-row"
{{ form.author.label }}*: <button type="button" data-toggle="fieldset-add-row"
data-target="#phone-fieldset">+</button>
<table>
<tr>
@ -25,28 +39,45 @@
</tr>
{% for author in form.author %}
<tr data-toggle="fieldset-entry">
<td>{{ author.author_name }}</td>
<td>{{ author.author_name (size=50, class="form-control") }}</td>
<td><button type="button" data-toggle="fieldset-remove-row" id="phone-{{loop.index0}}-remove">-</button></td>
</tr>
{% endfor %}
</table>
</div><br>
<div class="form-group" style="padding-bottom: 10px;">
Category: {{ form.category(size=20,
class="form-control") }}
</div>
<div class="form-group" style="padding-bottom: 10px;">
Year published: {{ form.year_published(size=8, class="form-control") }}
</div>
</table><br>
<br>
Category:* <br> {{ form.category(size=50, class="form-control") }} <br><br>
Year published: <br> {{ form.year_published(size=8, class="form-control") }} <br><br>
How different is this item to the rest of the collection?
Or is it more of the same? <br>
{{ form.sameness(min=0, max=100, oninput="outputUpdate(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="sameness" id="selected-sameness">{{ form.sameness.data }} </output> % different</span>
<br><hr align="left" style="width:40%;"><br>
Check the bibliography. How diverse are the references in this book? <br>
{{ form.diversity(min=0, max=100, oninput="outputUpdate2(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="diversity" id="selected-diversity">{{ form.diversity.data }} </output> % diverse</span>
<br><hr align="left" style="width:40%;"><br>
Check the writing. Who is speaking? Is the voice more often male or female? <br>
{{ form.gender(min=1, max=100, oninput="outputUpdate3(value)") }} &nbsp;
<span style="color: #d3d3d3;"><output for="diversity" id="selected-gender">{{ form.gender.data }} </output> % female</span>
<br><hr align="left" style="width:40%;"><br>
Who are you? {{ render_field(form.who)}}
<br><hr align="left" style="width:40%;"><br>
<div class="form-group" style="padding-bottom: 10px;">
Current file: {{ book.file }}
Current file: {{ book.file }} &nbsp;&nbsp;&nbsp;&nbsp; Upload new file: {{form.file}}
</div>
<div class="form-group" style="padding-bottom: 10px;">
Upload new file: {{form.file}}
</div>
<div class="form-group" style="padding-bottom: 10px;">
If uploading, write a new message: {{form.message(size=150, class="form-control") }}
If uploading, write a new message: <br>{{form.message(size=135, class="form-control") }}
</div>
<br>

6
app/templates/header.html

@ -3,10 +3,16 @@
<li><a href="{{ url_for('home') }}">Home</a></li>
<li><a href="{{ url_for('show_books') }}">Catalogue</a></li>
<li><a href="{{ url_for('show_stacks') }}">Stacks</a></li>
{%if light%}
{%else%}
<li><a href="{{ url_for('add_book') }}">Add Book</a></li>
<li><a href="{{ url_for ('add_stack') }}">Add Stack</a></li>
{%endif%}
<li><a href="{{ url_for('about') }}">About</a></li>
{%if light%}
{%else%}
<li><a href="{{ url_for('show_instances') }}">Instances</a></li>
{%endif%}
</ul>
<div class="clearfix"></div>
</nav>

11
app/templates/home.html

@ -3,10 +3,10 @@
{% block main %}
<div id="home_content">
<h1 class="header" id="title_xppl">XPPL</h1>
<p class="lead">This is the awesome library of Experimental Publishing. <br>
<p class="lead"> Welcome to our digital library. <br>
On instance: {{server}} / From: {{client}}
<br>
This might only be one interface to this library:
Feel free to browse our catalogue, interfaced in many different ways.
</p>
<a href="{{url_for('scape')}}">Scape</a>
@ -15,6 +15,8 @@ This might only be one interface to this library:
<br><br><br>
{%if light%}
{%else%}
<form method="GET" action="/export/csv">
<button type="submit">Export Catalogue (.CSV)</button>
</form>
@ -23,9 +25,13 @@ This might only be one interface to this library:
<button type = "button" onclick="document.getElementById('file_import_csv').click()">Import Catalogue (.CSV)</button>
</form>
</div>
{%endif%}
<div id="app" class="container">
<div class="messageback1"></div>
<div class="messageback2"></div>
<section class="messages">
<div v-for="message in messages" class="box">
<article class="media">
<div class="media-content">
@ -38,6 +44,7 @@ This might only be one interface to this library:
</div>
</article>
</div>
</section>
<section class="new-message">
<div class="field has-addons">

19
app/templates/red_link.html

@ -32,10 +32,13 @@
</div>
{% endif %}
{% endwith %}
{%if light%}
{%else%}
<div style="width: 220px; float: left; padding-right: 40px;">
<img class="no_cover" width="220" src = '/static/img/default_cover.gif';>
</div>
<br>
<div>
<h2>Add this book:</h2>
@ -60,21 +63,19 @@
</table>
</div>
</div>
<div style="padding-left:10px; padding-bottom: 50px;">
<div style="padding-left:10px; padding-bottom: 100px;">
<br>
Category: {{ form.category(size=27, class="form-control") }}
<br>
<br>
Year published: {{ form.year_published(size=8, class="form-control") }}
<br>
<br>
{{ form.file }}
{{ form.upload }}
{{ form.wish }}
</form>
<br>
<p><a href="{{ url_for('home') }}" style="font-size: 9pt;">go back home</a></p>
<br>
</div>
{%endif%}
<div class= "Container" style= "border-top: dashed; border-width: 1px;">
<h2> More potential books </h2>
<table class="library_table" id="table" style="width:100%">
@ -86,7 +87,10 @@
<th width="100px;">Year</th>
<th width="100px;">Category</th>
<th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to a stack</th>
{%endif%}
</tr>
</thead>
<tbody>
@ -94,7 +98,7 @@
<tr>
<td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65">
<p hidden="True"></p>
@ -121,10 +125,13 @@
</td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
===>
</a></td>
{%endif%}
</tr>
{% endfor %}

23
app/templates/results.html

@ -9,12 +9,12 @@
{{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button>
<p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form>
</div>
<br>
<h3 style="line-height:0px;"">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<h3 style="line-height:0px;">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<div style="height: 20px; background-color: yellow; line-height:4px;">
<div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div>
</div>
@ -39,13 +39,15 @@
<th width="100px;">Year</th>
<th width="100px;">Category</th>
<th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to stack</th>
{%endif%}
</tr>
{% for book in books %}
<tr>
<td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td>
<td> {% for author in book.authors %}
@ -60,9 +62,12 @@
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}">{{ stack.stack_name }}</a> </li>
{% endfor %}
</td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
==>
</a></td>
{%endif%}
{% endfor %}
</table>
@ -81,12 +86,14 @@
<th width="100px;">Year</th>
<th width="100px;">Category</th>
<th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to stack</th>
{%endif%}
</tr>
{% for book in books_all %}
<tr>
<td style= "padding: 5px;"><img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="80" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<td style= "padding: 5px;"><img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="80" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></td>
<td><a href="{{url_for('show_book_by_id', id=book.id)}}">{{ book.title }}</a></td>
<td> {% for author in book.authors %}
@ -102,10 +109,12 @@
{% endfor %}
</td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
==>
</a></td>
{%endif%}
{% endfor %}
</table>
<p>

10
app/templates/results_grid.html

@ -9,12 +9,12 @@
{{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button>
<p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form>
</div>
<br>
<h3 style="line-height:0px;"">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<h3 style="line-height:0px;">Results: "{{ query }}" included in {{ count }} out of {{ whole }} items </h3>
<div style="height: 20px; background-color: yellow; line-height:4px;">
<div style="background-color: black; width: {{ percentage }}%; height: 100%;"></div>
</div>
@ -37,7 +37,7 @@
<div class="gridbox">
<a href="/books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p>
<tbody>
<th>
@ -58,10 +58,10 @@
<div class="grid">
{% for book in books_all|sort(attribute='title', reverse = False) %}
<div class="gridbox">
<a href="books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p>
<tbody>
<th>

8
app/templates/scape.html

@ -74,7 +74,7 @@
<div class = "drag" id = "{{ book.id }}" style="position: absolute;width:70px;height:auto; top:{{ book.scapeY }}px; left:{{ book.scapeX }}px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.png') this.src = '../static/img/default_cover.png';">
<img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" style="width:100%;height:auto;" onerror="if (this.src != '../static/img/default_cover.gif') this.src = '../static/img/default_cover.gif';">
<p class="booktitle" style="font-size:7px;"><a href="books/{{ book.id }}">{{ book.title }}</a></p>
{% set got = {} %}
@ -104,7 +104,7 @@
</div>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
<script src="{{ url_for("static", filename="js/jquery-3.3.1.min.js") }}"></script>
<script src="{{ url_for("static", filename="js/app.js") }}"></script>
<script>
@ -150,8 +150,8 @@
}
</script>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<script src="{{ url_for("static", filename="js/jquery-ui-1.12.1.custom/jquery-ui.js") }}"></script>
<script>
$( function() {
var currentZoom = $('#scape_container').css('zoom');

84
app/templates/show_book_detail.html

@ -2,11 +2,12 @@
{% block main %}
<div class="container">
<h1 class="header">{{ book.title }}</h1>
<div style="float:right; padding-right: 140px;">
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="280px" onerror="if (this.src != '../uploads/cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"></div>
<img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="280px" onerror="if (this.src != '../cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';"></div>
<table class="library_table" id="table" style="width:50%; padding-bottom: 80px;">
<table class="library_table" id="table" style="width:50%; padding-bottom: 180px;">
<thead>
<tr id="header">
<th style="width: 150px;"></th>
@ -58,58 +59,57 @@
<td>Who is uploading<br></td>
<td>{{ book.who or '?' }} </td>
</tr>
<tr>
<td>How much time has been spent with this item?<br></td>
<td>{{ book.time or '?' }} </td>
</tr>
<td>Instances:</td>
<td>{% set got = {} %}
{% set all = 1 %}
{% for instance in book.instances %}
{% if instance.name in got %}
{% set x=got.__setitem__(instance.name, got[instance.name]+1) %}
{% else %}
{% set x=got.__setitem__(instance.name, 1) %}
{% endif %}
{% set all = loop.index %}
{% endfor %}
{% for instance, value in got.items() %}
{% set result = value/(book.instances|length) %}
{{ instance }}: {{ (result*100)|round|int }}%<br>
{% endfor %}</td>
</tbody>
</table>
<a href="../uploads/{{ book.file }}">download {{ book.fileformat }}</a>
{%if light%}
{%else%}
<br>
<a href="{{url_for('add_to_stack', id=book.id)}}">Add book to Stack ===></a> <br><br>
<br>
<p>Instances:</p>
{% set got = {} %}
{% set all = 1 %}
{% for instance in book.instances %}
{% if instance.name in got %}
{% set x=got.__setitem__(instance.name, got[instance.name]+1) %}
{% else %}
{% set x=got.__setitem__(instance.name, 1) %}
{% endif %}
{% set all = loop.index %}
{% endfor %}
{% if book.file %}
<button id="myBtn" style= "width: 180px; font-size: 10pt;"><a> Download this {{ book.fileformat }}</a></button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h3>A message from the uploading librarian:</h3>
<span style="font-style: italic;">"{{book.message or '...'}}" </span><br>
<h4><a href="../uploads/{{ book.file }}"> >>>> Link to file <<<<</h4></a>
</div>
</div>
{% else %}
{% endif %}
{% for instance, value in got.items() %}
{% set result = value/(book.instances|length) %}
{{ instance }}: {{ (result*100)|round|int }}%<br>
{% endfor %}
<br>
{% if book.file %}
<button id="myBtn" style= "width: 180px; font-size: 10pt;"><a> Download this {{ book.fileformat }}</a></button>
<div id="myModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h3>A message from the uploading librarian:</h3>
<span style="font-style: italic;">"{{book.message or 'Happy reading.'}}" </span><br>
<h4><a href="../uploads/{{ book.file }}"> >>>> Link to file <<<<</h4></a></div>
</div>
{% else %}
{% endif %}
<button style= "font-size: 10pt;"> <a href="{{ url_for('edit_book_by_id', id=book.id )}}">edit</a></button>
<button style= "font-size: 10pt;"> <a href="{{ url_for('remove_book_by_id', id=book.id)}}">delete</a></button>
<a href="{{ url_for('edit_book_by_id', id=book.id )}}"><button style= "font-size: 10pt;"> edit</button></a>
<a href="{{ url_for('remove_book_by_id', id=book.id)}}"><button style= "font-size: 10pt;"> delete</button></a>
<br><br>
<br><br>
{%endif%}
<hr>
{% if previousbook %}
<a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 9pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %}
<a href="{{ url_for('show_book_by_id', id=previousbook.id )}}" style="font-size: 10pt;"> < see the previous book added to XPPL: &nbsp;<i>{{ previousbook.title |truncate(40,True,'...') }} </i></a> {% endif %}
{% if nextbook %}
<a href="{{ url_for('show_book_by_id', id=nextbook.id )}}" style="float:right; font-size: 9pt;"> see the next book added to XPPL: &nbsp;<i>{{ nextbook.title|truncate(40,True,'...')}} </i>> </a>{% endif %}
<a href="{{ url_for('show_book_by_id', id=nextbook.id )}}" style="float:right; font-size: 10pt;"> see the next book added to XPPL: &nbsp;<i>{{ nextbook.title|truncate(40,True,'...')}} </i>> </a>{% endif %}
</div>
{% endblock %}

21
app/templates/show_books.html

@ -3,7 +3,7 @@
{% block main %}
<div class="container">
{% from "_formhelpers.html" import render_field %}
<form method="POST">
<div>{{ form.select(style="width: 100px; margin: 10px; float: left; font-size: 20px") }}</div>
<div class="search">
@ -36,7 +36,10 @@
<th width="100px;">Year</th>
<th width="100px;">Category</th>
<th width="100px;">Stack</th>
{%if light%}
{%else%}
<th width="100px;">Add to a stack</th>
{%endif%}
</tr>
</thead>
<tbody>
@ -44,7 +47,7 @@
<tr>
<td style= "padding: 5px;">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="70" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="70" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';">
<!-- <object class="no_cover" data="../static/img/default_cover.png" type="image/png" width="65">
<p hidden="True"></p>
@ -59,19 +62,29 @@
{% endfor %}</td>
<td id='fileformat'>{{ book.year_published or '––'}}</td>
<td>{{ book.category}}</td>
<td>
<td id='ascii'>
{% for stack in book.stacks %}
<li><a href="{{url_for('show_stack_by_id', id=stack.id)}}"> {{ stack.stack_name }} </a></li>
{% else %}
<li style='font-size:10px;'>
XXXXXXXXXX <br>
XXXXXXXXXX <br>
XXXXXXXXXX <br>
{% endfor %}
</td>
{%if light%}
{%else%}
<td id='plus'><a href="{{url_for('add_to_stack', id=book.id)}}">
===>
</a></td>
</a></td>{%endif%}
</tr>
{% endfor %}

4
app/templates/show_books_grid.html

@ -9,7 +9,7 @@
{{ render_field(form.search) }} </div>
<button type="submit" class="button">browse</button>
<p><br>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
{{ form.grid(style="font-size:20px")}}{{ form.listview(style="font-size:20px")}}</p>
</form>
</div>
@ -34,7 +34,7 @@
<div class="gridbox">
<a href="books/{{ book.id }}">
<img class="no_cover" id="{{ book.title }}" src="/uploads/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '//uploads/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<img class="no_cover" id="{{ book.title }}" src="/cover/{{ book.cover }}" width="100%" onerror="if (this.src != '/cover/{{ book.cover }}') this.src = '/static/img/default_cover.gif';"></a>
<p>
<tbody>
<th>

33
app/templates/show_stack_detail.html

@ -6,33 +6,46 @@
<h1 class="header">{{ stack.stack_name }}</h1>
<p>{{ stack.stack_description }} </p>
<p id='creator'>Created by:
<p id='creator'>Created by:
{% if stack.stack_author == None %} anon</p>
{% else %}
{{ stack.stack_author }}
{% endif %}
<h2>Books in this stack:</h2>
<p>
{% for book in stack.books %}
{% if loop.length >= 5 %}
{% if loop.index == 6 %}
<p style='color:red;'>Your stack is getting a little too big. Are these next books really relevant?</p>
<br>
<br>
{% endif %}
{% endif %}
<li> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li>
<img class="no_cover" id="{{ book.title }}" src="../uploads/cover/{{ book.cover }}" width="150" onerror="if (this.src != '../uploads/cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
<img class="no_cover" id="{{ book.title }}" src="../cover/{{ book.cover }}" width="150" onerror="if (this.src != '../cover/{{ book.cover }}') this.src = '../static/img/default_cover.gif';">
<div class='widget'>
{%if light%}
{%else%}
<iframe src="../uploads/{{ book.file }}" width="50%" ></iframe>
{%endif%}
</div>
{% endfor %}</p>
{% endfor %}</p>
<br>
<br>
<p>
<a href="{{ url_for('remove_stack_by_id', id=stack.id )}}">Remove stack</a> </p>
<p>
<a href="{{ url_for('edit_stack_by_id', id=stack.id )}}">Edit title and/or description</a> </p>
{%if light%}
{%else%}
<p><a href="{{url_for('show_books')}}">Add some more books</a></p>
<p><a href="{{ url_for('edit_stack_by_id', id=stack.id )}}">Edit title and/or description</a> </p>
<p><a href="{{ url_for('remove_stack_by_id', id=stack.id )}}">Remove stack</a> </p>
{%endif%}
<p><a href="{{url_for('show_stacks')}}">Go back to the other stacks</p>
</div>

11
app/templates/show_stack_detail_tab.html

@ -17,17 +17,12 @@
{{ stack.stack_author }}
{% endif %}
<p style='font-weight:bold;'>Books in this stack: {% for book in stack.books %}
<p style='font-weight:bold;'>Books in this stack: {% for book in stack.books %} </p>
<li style="font-size: 18px;"> <a href="{{url_for('show_book_by_id', id=book.id)}}">{{book.title}}</a> </li>
<p style="font-size: 10px;"><a href='{{url_for('add_to_stack', id=book.id)}}'>
Add to another stack
</a></p>
{% endfor %}</p>
<p style="font-size: 10px;"><a href='{{url_for('add_to_stack', id=book.id)}}'> Add to another stack </a></p>
{% endfor %}
</div>

156
app/views.py

@ -5,9 +5,10 @@ Werkzeug Documentation: http://werkzeug.pocoo.org/documentation/
This file creates your application.
"""
from app import app, db, socketio, DOMAIN
from app import app, db, socketio, DOMAIN, light
from flask import Flask, Response, session, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort
import json
from functools import wraps
from sqlalchemy.sql.expression import func, select
from sqlalchemy.sql import except_
from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm
@ -48,26 +49,34 @@ def allowed_file(filename):
# Routing for your application.
###
def check_light(func):
@wraps(func)
def decorated_function(*args, **kwargs):
if not light:
return func(*args, **kwargs)
else:
flash("Your account has expired. Update your billing info.")
return redirect(url_for('home'))
return decorated_function
@app.route('/', methods= ['POST','GET'])
def home():
print("/////////////")
print(light)
chat_form = ChatForm()
chat_messages = db.session.query(Chat).all()
username = 'librarian'
# if request.method == 'POST':
# if chat_form.validate_on_submit():
# message = chat_form.message.data
# msg = Chat(message)
# db.session.add(msg)
# db.session.commit()
#client = request.remote_addr
server = request.host
if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
client =request.environ['REMOTE_ADDR']
else:
client = request.environ['HTTP_X_FORWARDED_FOR']
return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username, client=client, server=server)
return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username=username, client=client, server=server, light=light)
@app.route('/hello/<name>')
def hello(name):
@ -76,9 +85,10 @@ def hello(name):
@app.route('/about/')
def about():
"""Render the website's about page."""
return render_template('about.html', name="Mary Jane")
return render_template('about.html', light=light)
@app.route('/uploads/<filename>')
@check_light
def uploaded_file(filename):
book = Book.query.filter_by(file=filename).first()
i = Instance(request.host, "download")
@ -90,7 +100,7 @@ def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'],
filename)
@app.route('/uploads/cover/<filename>')
@app.route('/cover/<filename>')
def uploaded_file_cover(filename):
return send_from_directory(app.config['UPLOAD_FOLDER_COVER'],
filename)
@ -131,7 +141,7 @@ def scape():
@app.route('/books_grid')
def show_books_grid():
books = db.session.query(Book).all() # or you could have used User.query.all()
return render_template('show_books_grid.html', books=books)
return render_template('show_books_grid.html', books=books, light=light)
@app.route('/books/<int:id>')
def show_book_by_id(id):
@ -155,12 +165,13 @@ def show_book_by_id(id):
db.session.add(user_info)
db.session.commit()
if not book:
return render_template('red_link.html', id=id)
return render_template('red_link.html', id=id, light=light)
else:
return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook, all_instances=all_instances)
return render_template('show_book_detail.html', book=book, previousbook = previousbook, nextbook = nextbook, all_instances=all_instances, light=light)
@app.route('/books/<int:id>/delete', methods=['POST', 'GET'])
@check_light
def remove_book_by_id(id):
book_to_edit = Book.query.filter_by(id=id).first()
title = book_to_edit.title
@ -171,9 +182,10 @@ def remove_book_by_id(id):
return redirect(url_for('show_books'))
@app.route('/books/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_book_by_id(id):
book_to_edit = Book.query.filter_by(id=id).first()
user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published, message= book_to_edit.message)
user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published, message= book_to_edit.message, sameness=book_to_edit.sameness, gender=book_to_edit.gender, diversity=book_to_edit.diversity, who=book_to_edit.who)
if request.method == 'POST':
if user_form.validate_on_submit():
@ -183,6 +195,11 @@ def edit_book_by_id(id):
category = user_form.category.data
year_published = user_form.year_published.data
message = user_form.message.data
sameness = user_form.sameness.data
gender = user_form.gender.data
diversity = user_form.diversity.data
who = user_form.who.data
if year_published=="":
year_published = None
book = Book.query.filter_by(id=id).first()
@ -190,6 +207,10 @@ def edit_book_by_id(id):
book.category = category
book.year_published = year_published
book.message = message
book.sameness = sameness
book.gender = gender
book.diversity = diversity
book.who = who
#authors update
book.authors.clear()
@ -230,10 +251,11 @@ def edit_book_by_id(id):
flash("%s updated" % (title))
return redirect(url_for('show_book_by_id', id=id))
return render_template('edit_book_detail.html', book=book_to_edit, form=user_form)
return render_template('edit_book_detail.html', book=book_to_edit, form=user_form, light=light)
@app.route('/add-book', methods=['POST', 'GET'])
@check_light
def add_book():
upload_form = UploadForm()
allbooks = db.session.query(Book).all()
@ -259,7 +281,6 @@ def add_book():
sameness = upload_form.sameness.data
gender = upload_form.gender.data
diversity = upload_form.diversity.data
time = upload_form.time.data
who = upload_form.who.data
if year_published=="":
@ -309,9 +330,8 @@ def add_book():
html_string = render_template('potential_pdf.html', pbooks = pbooks)
html = HTML(string=html_string)
html.write_pdf(target='app/uploads/potential.pdf');
print ('potential_pdf')
book = Book(title, filename, cover, file_extension, category, year_published, message, sameness, diversity, gender, who, time)
book = Book(title, filename, cover, file_extension, category, year_published, message, sameness, diversity, gender, who)
db.session.add(book)
for author in authors:
author_name = author.get("author_name")
@ -332,7 +352,7 @@ def add_book():
return redirect(url_for('show_books'))
flash_errors(upload_form)
return render_template('add_book.html', form=upload_form, books_all=books_all, authors_all=authors_all, categories=categories, stacks_all=stacks_all, books_potential=books_potential, earliest=earliest, latest=latest)
return render_template('add_book.html', form=upload_form, books_all=books_all, authors_all=authors_all, categories=categories, stacks_all=stacks_all, books_potential=books_potential, earliest=earliest, latest=latest, light=light)
# Flash errors from the form if validation fails
@ -352,10 +372,11 @@ def show_author_by_id(id):
if not author:
abort (404)
else:
return render_template('show_author_detail.html', author=author)
return render_template('show_author_detail.html', author=author, light=light)
@app.route('/authors/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_author_by_id(id):
return "Ask the programmer."
@ -364,9 +385,10 @@ def edit_author_by_id(id):
@app.route('/stacks')
def show_stacks():
stacks = db.session.query(Stack).all()
return render_template('show_stacks.html', stacks=stacks)
return render_template('show_stacks.html', stacks=stacks, light=light)
@app.route('/stacks/add_stack', methods=['POST', 'GET'])
@check_light
def add_stack():
form = StackForm()
stacks = db.session.query(Stack).all()
@ -382,7 +404,7 @@ def add_stack():
stacks = db.session.query(Stack).all()
return redirect(url_for('show_stacks'))
flash("%s stack created" % (stack_name))
return render_template('add_stack.html', stacks=stacks, form=form)
return render_template('add_stack.html', stacks=stacks, form=form, light=light)
@app.route('/stacks/tab/<int:id>', methods=['POST', 'GET'])
def show_stack_in_tab(id):
@ -394,20 +416,22 @@ def show_stack_by_id(id, is_tab=False):
stack = Stack.query.get(id)
if not stack:
abort (404)
return render_template('add_stack.html', stacks=stacks, form=form, light=light)
else:
if is_tab == False:
return render_template('show_stack_detail.html', stack=stack)
return render_template('show_stack_detail.html', stack=stack, light=light)
else:
return render_template('show_stack_detail_tab.html', stack=stack)
return render_template('show_stack_detail_tab.html', stack=stack, light=light)
@app.route('/stacks/<int:id>/delete', methods=['POST', 'GET'])
@check_light
def remove_stack_by_id(id):
Stack.query.filter_by(id=id).delete()
db.session.commit()
return redirect(url_for('show_stacks'))
@app.route('/stacks/<int:id>/edit', methods=['POST', 'GET'])
@check_light
def edit_stack_by_id(id):
stack = Stack.query.filter_by(id=id).first()
form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description)
@ -420,9 +444,10 @@ def edit_stack_by_id(id):
stack.stack_description = stack_description
db.session.commit()
return redirect(url_for('show_stack_by_id', id=id))
return render_template('edit_stack_detail.html', stack=stack, form=form)
return render_template('edit_stack_detail.html', stack=stack, form=form, light=light)
@app.route('/instances', methods=['POST', 'GET'])
@check_light
def show_instances():
all_instances = db.session.query(Instance).all()
instances = []
@ -451,17 +476,18 @@ def show_instances():
print(oldname)
print(name)
db.session.commit()
return render_template('show_instances.html', instances=instances)
return render_template('show_instances.html', instances=instances, light=light)
@app.route('/stacks/<int:stackid>/remove/<int:bookid>', methods=['POST', 'GET'])
@check_light
def remove_from_stack(bookid, stackid):
book = Book.query.get(bookid)
stack = Stack.query.get(stackid)
if book not in stack.books:
return render_template('show_book_detail.html', book=book)
return render_template('show_book_detail.html', book=book, light=light)
stack.books.remove(book)
db.session.commit()
return render_template('show_book_detail.html', book=book)
return render_template('show_book_detail.html', book=book, light=light)
## search
view = ['1']
@ -476,12 +502,12 @@ def show_books():
if search.grid.data:
viewby = '2'
view.append('2')
return render_template ('show_books_grid.html', books=books, form=search)
return render_template ('show_books_grid.html', books=books, form=search, light=light)
if search.listview.data:
viewby = '1'
view.append('1')
return render_template ('show_books.html', books=books, form=search)
return render_template ('show_books.html', books=books, form=search, light=light)
if request.method == 'POST':
newmsg = 'searched for: ' + search.search.data
@ -498,7 +524,7 @@ def show_books():
db.session.rollback()
return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby)))
return render_template('show_books.html', books=books, form=search)
return render_template('show_books.html', books=books, form=search, light=light)
@app.route('/search/<searchtype>/<viewby>/<query>', methods=['POST', 'GET'])
def search_results(searchtype, query, viewby):
@ -519,19 +545,21 @@ def search_results(searchtype, query, viewby):
if searchtype== 'Stack':
results=db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)).order_by(Book.title)
if searchtype== 'Outliers':
results=Book.query.filter(Book.sameness > 50).order_by(Book.title)
if searchtype== 'All':
# results=Book.query.whoosh_search(query)
results=Book.query.filter(Book.title.contains(query))
results=results.union(Book.query.filter(Book.category.contains(query)))
results=results.union(Book.query.filter(Book.year_published.contains(query)))
results=results.union(db.session.query(Book).join(Book.authors).filter(Author.author_name.contains(query)))
results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query)))
results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_description.contains(query))).order_by(Book.title)
results=results.union(db.session.query(Book).join(Book.stacks).filter(Stack.stack_name.contains(query))).order_by(Book.title)
if results.count() == 0:
books = Book.query.filter(Book.file.like('potential.pdf'))
upload_form = UploadForm(title= query, author='')
return render_template('red_link.html', form=upload_form, title=query, books=books)
return render_template('red_link.html', form=upload_form, title=query, books=books, light=light)
count = results.count()
whole = Book.query.count()
@ -541,11 +569,11 @@ def search_results(searchtype, query, viewby):
if search.listview.data:
view.append('1')
return render_template('results.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage)
return render_template('results.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage, light=light)
if search.grid.data:
view.append('2')
return render_template('results_grid.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage)
return render_template('results_grid.html', books=results, form=search, query=query, books_all=books_all, searchtype=search.select.data, count = count, whole = whole, percentage = percentage, light=light)
if request.method == 'POST':
newmsg = 'searched for: ' + search.search.data
@ -570,10 +598,10 @@ def search_results(searchtype, query, viewby):
return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data, viewby=viewby)))
if viewby == '2':
return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage)
return render_template('results_grid.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage, light=light)
else:
return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage)
return render_template('results.html', form=search, books=results, books_all=books_all, searchtype=search.select.data, query=query, count = count, whole = whole, percentage = percentage, light=light)
autocomplete_suggestions = []
autocomplete.load()
@ -599,22 +627,24 @@ def autocomplete_search():
## STACKS!
@app.route('/add_to_stack/<int:id>', methods=['GET', 'POST'])
@check_light
def add_to_stack(id):
stacks = db.session.query(Stack).all()
add_form = AddtoStackForm(request.form)
add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks]
if request.method == 'GET':
book = Book.query.get(id)
return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form)
return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form, light=light)
else:
stack = Stack.query.get(int(add_form.select_stack.data))
book = Book.query.get(id)
stack.books.append(book)
db.session.commit()
return render_template('show_stack_detail.html', stack=stack)
return render_template('show_stack_detail.html', stack=stack, light=light)
@app.route('/export/csv', methods=['GET'])
@check_light
def export_csv():
output = io.StringIO()
#fieldnames = ['title', 'authors', 'file', 'fileformat', 'category', 'year_published', 'description' ]
@ -650,6 +680,7 @@ def export_csv():
import codecs
@app.route('/import/csv', methods= ['POST','GET'])
@check_light
def import_csv():
if request.method == 'POST':
if 'file' not in request.files:
@ -664,19 +695,35 @@ def import_csv():
print("allreadyexists")
else:
cover = ''
if row['file']:
if row['file'] == '':
file = 'potential.pdf'
file_extension = '.pdf'
ptitle = row['title']
pbook = Potential(ptitle)
db.session.add(pbook)
db.session.commit()
pbooks = Potential.query.all()
template = 'app/templates/potential_pdf.html'
html_string = render_template('potential_pdf.html', pbooks = pbooks)
html = HTML(string=html_string)
html.write_pdf(target='app/uploads/potential.pdf')
else:
fullpath = os.path.join(app.config['UPLOAD_FOLDER'], row['file'])
name, file_extension = os.path.splitext(row['file'])
print ('get_cover', fullpath, name)
cover = get_cover(fullpath, name)
file = str(id) + "_" + row['file']
if row['year_published']:
year_published = int(row['year_published'])
else:
year_published = None;
book = Book(row['title'], row['file'], cover, row['fileformat'], row['category'],year_published, None, None, None, None, None, None)
book.scapeX = float(row['scapeX'])
book.scapeY = float(row['scapeY'])
book = Book(row['title'], file, cover, row['fileformat'], row['category'],year_published, None, None, None, None, None)
if row['scapeX']:
book.scapeX = float(row['scapeX'])
if row['scapeY']:
book.scapeY = float(row['scapeY'])
db.session.add(book)
numberadded = numberadded+1
@ -690,10 +737,11 @@ def import_csv():
db.session.add(a)
book.authors.append(a)
db.session.commit()
return render_template('import_csv.html', numberadded=numberadded)
return render_template('import_csv.html', numberadded=numberadded, light=light)
@app.route('/empty_catalogue487352698237465', methods= ['POST','GET'])
@app.route('/emptycataloguexpubxpubfuck', methods= ['POST','GET'])
@check_light
def empty_catalogue():
meta = db.metadata
for table in reversed(meta.sorted_tables):
@ -737,12 +785,6 @@ def get_chat():
# The functions below should be applicable to all Flask apps.
###
@app.route('/<file_name>.txt')
def send_text_file(file_name):
"""Send your static text file."""
file_dot_text = file_name + '.txt'
return app.send_static_file(file_dot_text)
@app.after_request
def add_header(response):
@ -751,14 +793,14 @@ def add_header(response):
and also to cache the rendered page for 10 minutes.
"""
response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
response.headers['Cache-Control'] = 'public, max-age=600'
response.headers['Cache-Control'] = 'no-cache'
return response
@app.errorhandler(404)
def page_not_found(error):
"""Custom 404 page."""
return render_template('404.html'), 404
return render_template('404.html', light=light), 404
### SOCKET for the chat

2
import_csv.py

@ -20,7 +20,7 @@ with open(args.csv) as f:
print ('get_cover', fullpath, name)
cover = get_cover(fullpath, name)
book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None, None, None, None, None, None, None)
book = Book(row['Title'], row['Filename'], cover, row['Format'], row['Category'], None, None, None, None, None, None)
db.session.add(book)
authors = row['Author'].split(',')

2
init_db.sh

@ -1,5 +1,5 @@
#!/bin/bash
rm app/mydatabase.db
#rm app/mydatabase.db
mkdir -p app/uploads/cover
chmod 777 app/uploads/
chmod 777 app/uploads/cover

BIN
whoosh/Book/MAIN_071mbyqoitijmyv4.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_2a7vygakwdvdefoe.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_5id3ip2mvmsu2mbl.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_6bsjkp9xdes7zrrx.seg

Binary file not shown.

0
whoosh/Book/MAIN_WRITELOCK

BIN
whoosh/Book/MAIN_es8t5xyup7nfqm7j.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_hljrpqpa46dk3vgc.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_p6r8oedtat7ay25v.seg

Binary file not shown.

BIN
whoosh/Book/MAIN_q0ohjvxyt5ufv1vl.seg

Binary file not shown.

BIN
whoosh/Book/_MAIN_293.toc

Binary file not shown.
Loading…
Cancel
Save