2022-03-29 14:44:03 +02:00
# THIS SOFTWARE IS A PART OF FREE COMPETITOR PROJECT
# THE FOLLOWING SOURCE CODE I UNDER THE GNU
# AGPL LICENSE V3 OR ANY LATER VERSION.
# This project is not for simple users, but for
# web-masters and a like, so we are counting on
# your ability to set it up and running.
2022-04-04 18:54:11 +02:00
import os
2022-04-16 12:37:13 +02:00
import time
2022-03-29 14:44:03 +02:00
from modules import search
2022-04-16 12:37:13 +02:00
from modules import missing
2022-04-02 00:10:18 +02:00
2022-04-11 21:18:21 +02:00
2022-03-29 14:44:03 +02:00
def html ( page , json ) :
# This function adds a rendering of the json into the page
2022-04-01 21:59:32 +02:00
free = search . is_free ( json )
2024-04-29 18:17:57 +02:00
name = json . get ( " names " , [ " Unknown " ] ) [ 0 ]
2022-04-14 21:30:22 +02:00
page = page + """
< ! - - For each software in the list we do mostly the same things .
And here it is . First we show it ' s name as a link to be able
to search Free Competitors to what is found . - - >
"""
2024-04-29 18:17:57 +02:00
page = page + " \n <a href= \" / " + name . replace ( " " , " + " ) . lower ( ) + " \" > "
page = page + " \n <h1 class= \" software-heading \" > "
2022-03-29 14:44:03 +02:00
try :
2024-04-29 18:17:57 +02:00
page = page + ' \n <span class= " software-logo " ><img src= " ' + json [ " links " ] [ " icon " ] + ' " alt= " [LOGO] " style= " height:50px; " ></span> '
2022-03-29 14:44:03 +02:00
except :
pass
2022-04-14 21:30:22 +02:00
2024-04-29 18:17:57 +02:00
page = page + " <span class= \" software-name \" > " + name + " </span> \n "
2022-04-14 21:30:22 +02:00
page = page + " </h1> \n </a> \n \n "
page = page + """
< ! - - Next there is a short paragraph about the program .
2024-04-29 18:17:57 +02:00
( Often Copypasted from some official page about it ) .
2022-04-14 21:30:22 +02:00
Which means , it may contain the terrible words " Open Source "
that hide the fact that Free Software is about User Freedom
first of all . So we are trying to link to a special page on
GNU . ORG if such a thing is found - - >
"""
2024-04-29 18:17:57 +02:00
2022-03-29 14:44:03 +02:00
# Few words about it
2024-04-29 18:17:57 +02:00
comment = json . get ( " comment " , " " )
2022-04-03 02:41:46 +02:00
not_foss = [ ' open source ' , ' open-source ' ]
if " open source " or " open-source " in comment . lower ( ) :
2022-04-02 13:11:49 +02:00
# Well... Here is a thing. Free Software, not open source.
where = comment . lower ( ) . find ( " open source " )
2022-04-03 02:41:46 +02:00
# In case it has a slash in it.
if where == - 1 :
where = comment . lower ( ) . find ( " open-source " )
2024-04-29 18:17:57 +02:00
ops = comment [ where : where + 11 ]
2022-04-14 21:30:22 +02:00
if ops :
comment = comment . replace ( ops ,
2024-04-29 18:17:57 +02:00
" <a href= \" https://www.gnu.org/philosophy/open-source-misses-the-point.en.html \" > " + ops + " </a> " )
page = page + " \n <p class= \" software-comment \" > \n " + comment + " \n </p> \n \n "
2022-03-29 14:44:03 +02:00
2022-03-31 20:08:42 +02:00
# I want to show nothing else from if it's proprietary
2022-04-04 18:54:11 +02:00
issues_files = list ( os . listdir ( " data/issues " ) )
2022-05-12 17:54:52 +02:00
if " issues " in json and json [ " issues " ] :
2022-03-31 20:08:42 +02:00
l = json . get ( " issues " , [ ] )
2024-04-29 18:17:57 +02:00
page = page + " <h2>Anti-Features / Problems:</h2> "
page = page + " <ul class= \" anti-features \" > "
2022-03-31 20:08:42 +02:00
for i in l :
2024-04-29 18:17:57 +02:00
if i + " .html " not in issues_files :
page = page + " <li> " + i + " </li> "
2022-04-04 18:54:11 +02:00
else :
2024-04-29 18:17:57 +02:00
page = page + ' <li><details title= " Read about ' + i + ' " > '
page = page + " <summary> " + i + " </summary> "
issuefile = open ( " data/issues/ " + i + " .html " )
page = page + " <span><p> " + issuefile . read ( ) + " </p></span> "
page = page + " </details></li> "
page = page + " </ul> "
2022-04-02 17:28:29 +02:00
if not free :
2022-03-31 20:08:42 +02:00
return page
2022-03-29 14:44:03 +02:00
# Links
2022-04-14 21:30:22 +02:00
page = page + """
< ! - - And now the table itself . - - >
2022-03-30 18:12:06 +02:00
"""
2024-04-29 18:17:57 +02:00
linksfilter = { " git " : " source code " , " fsd " : " Free Software Directory " }
2022-03-31 20:08:42 +02:00
links = json . get ( " links " , { } )
for website in links :
if website in [ " icon " ] :
continue
link = links [ website ]
2022-03-31 19:16:16 +02:00
page = page + """
2022-04-14 21:30:22 +02:00
< ! - - Here ' s how to do a simple button -->
2024-04-29 18:17:57 +02:00
< a class = " button " href = \""" " + link + """ \" title= \" " " " + link + """ \" > """ + linksfilter . get ( website ,
website ) . upper ( ) + """ </a>
"""
2022-03-31 19:16:16 +02:00
2022-04-14 21:30:22 +02:00
page = page + """
< ! - - Details are those little collapsable things that I like to
use very much . It ' s very simple really. Just read the code
carefully and you will get it - - >
"""
2022-03-30 18:12:06 +02:00
2024-04-29 18:17:57 +02:00
# Details
categories = { " generic_name " : " Features " ,
" licenses " : " License(s) " ,
" platforms " : " Platforms " ,
" networks_read " : " Accesses Data from " ,
" networks_write " : " Interacts / Publishes to " ,
" formats_read " : " Opens from File-Formats " ,
" formats_write " : " Saves to File-Formats " ,
" interface " : " Interface " ,
" languages " : " Programming Languages " }
2022-03-30 18:12:06 +02:00
for c in categories :
l = json . get ( c , [ ] )
if not l :
continue
2022-04-07 19:22:31 +02:00
# I want to look whether this category has a list of files
alldata = list ( os . listdir ( " data " ) )
allfiles = [ ]
for folder in alldata :
if c . startswith ( folder ) :
try :
2024-04-29 18:17:57 +02:00
allfiles = list ( os . listdir ( " data/ " + folder ) )
2022-04-07 19:22:31 +02:00
break
except :
pass
2024-04-29 18:17:57 +02:00
2022-04-07 19:22:31 +02:00
# Count matches
matches = 0
for i in l :
if i . startswith ( " * " ) :
matches + = 1
2024-04-29 18:17:57 +02:00
2022-04-07 19:22:31 +02:00
if matches :
2024-04-29 18:17:57 +02:00
matchtext = " <i>( " + str ( matches ) + " )</i> "
2022-04-07 19:22:31 +02:00
else :
matchtext = " "
2024-04-29 18:17:57 +02:00
2022-04-14 21:30:22 +02:00
page = page + " \n \n <details> "
2024-04-29 18:17:57 +02:00
page = page + " \n <summary> " + categories [ c ] + " : " + matchtext + " </summary> "
2022-03-30 18:12:06 +02:00
for i in l :
2022-04-07 19:22:31 +02:00
matchtext = " "
if i . startswith ( " * " ) :
i = i [ 1 : ]
matchtext = " <i>( match )</i> "
2022-04-14 21:30:22 +02:00
2024-04-29 18:17:57 +02:00
if i + " .html " in allfiles :
datapage = open ( " data/ " + folder + " / " + i + " .html " )
page = page + """
< ! - - Just so happened that about \""" " + i + """ \" we had a file
in data / """ + folder + """ folder . So why not make a detail inside a detail ,
a detail - sception , so to speak . And add the text of explanation into it . - - >
"""
2022-04-14 21:30:22 +02:00
page = page + " <details> \n "
2024-04-29 18:17:57 +02:00
page = page + " <summary> " + matchtext + i + " </summary> \n "
2022-04-14 21:30:22 +02:00
page = page + " <span> \n <p> \n "
2024-04-29 18:17:57 +02:00
page = page + " " + datapage . read ( ) + " \n "
2022-04-14 21:30:22 +02:00
page = page + " </p> \n </span> \n </details> "
2022-04-07 19:22:31 +02:00
else :
2024-04-29 18:17:57 +02:00
page = page + " \n <span> " + matchtext + i + " </span> \n <br> \n "
2022-04-14 21:30:22 +02:00
page = page + """
< ! - - Just a tiny space after all the spans . So no to feel
too crowded , so to speak - - >
< span > < br > < / span >
< / details >
"""
2024-04-29 18:17:57 +02:00
2022-03-29 14:44:03 +02:00
return page
2024-04-29 18:17:57 +02:00
def suggestions ( page , json ) :
2022-03-29 14:44:03 +02:00
# This function will render suggestions
2022-04-14 21:30:22 +02:00
page = page + """
< ! - - == == == == == == == == == == == == == == == == == == == == == == == == == == == == == =
This is where ther actual competitors are starting to show ! ! !
== == == == == == == == == == == == == == == == == == == == == == == == == == == == == == - - >
2024-04-29 18:17:57 +02:00
< div class = " suggestions-container " >
< h1 class = " competitors-heading " > Free Competitors : < / h1 >
2022-04-14 21:30:22 +02:00
"""
2022-03-29 14:44:03 +02:00
found = search . suggest ( json )
2022-04-01 21:59:32 +02:00
biggest = 0
for i in found :
if i [ 0 ] > biggest :
biggest = i [ 0 ]
2022-04-02 17:28:29 +02:00
more = False
2022-03-29 14:44:03 +02:00
for i in found :
2022-04-01 21:59:32 +02:00
free = search . is_free ( i [ - 1 ] )
2022-03-30 19:52:36 +02:00
2022-04-08 18:11:38 +02:00
if not i [ 0 ] or i [ - 1 ] [ " names " ] == json [ " names " ] or not free :
2022-03-29 14:44:03 +02:00
continue
2022-04-02 17:28:29 +02:00
try :
frac = int ( i [ 0 ] / biggest * 100 )
except :
frac = 0
2022-04-17 13:53:24 +02:00
if frac < 10 and not more : # Below 10% features match
2022-04-14 21:30:22 +02:00
page = page + """
2022-04-17 13:53:24 +02:00
< ! - - Sometimes the suggestion is not very good . Below 10 %
2022-04-14 21:30:22 +02:00
of suggestion score . But it still kind of valid . So we
want to put it into the page . Only when the user clicks
something . Why not using the same old details ? - - >
< hr >
< details >
2024-04-29 18:17:57 +02:00
< summary > < h2 class = " problematic-heading " title = " Click to show more / less. " > Problematic Competitors : < / h2 > < / summary >
2022-04-14 21:30:22 +02:00
"""
2022-04-02 17:28:29 +02:00
more = True
2022-04-14 21:30:22 +02:00
page = page + """
2022-04-16 08:30:50 +02:00
< br > < br >
2024-04-29 18:17:57 +02:00
< ! - - == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == == - - >
2022-04-14 21:30:22 +02:00
"""
2022-04-16 08:30:50 +02:00
page = progress ( page , frac / 100 , " Suggestion score: " + str ( frac ) + " % " )
2022-03-29 14:44:03 +02:00
page = html ( page , i [ - 1 ] )
2022-04-02 17:28:29 +02:00
if more :
page = page + " </details> "
2024-04-29 18:17:57 +02:00
page = page + " </div> " # Close the suggestions-container
2022-03-30 18:12:06 +02:00
return page
2024-04-29 18:17:57 +02:00
def search_widget ( page , address ) :
2022-03-30 18:12:06 +02:00
# Adds a search bar to the page
page = page + """
2022-04-14 21:30:22 +02:00
< ! - - Search widget ! This widget makes it possible to implement
a search feature without using a single line of JavaScript code .
In HTML , there is an input field that we can use . If we pare it
with a button into a < form > , we can get a button that activates
the search . - - >
2022-04-03 18:27:21 +02:00
< form action = """
page = page + address
2024-04-29 18:17:57 +02:00
page = page + """ search method= " GET " class= " search-form " >
< input type = " text " name = " item " class = " search-input " placeholder = " Name of Software " >
< button type = " submit " class = " search-button " > Search < / button >
2022-03-30 18:12:06 +02:00
< / form >
2022-04-14 21:30:22 +02:00
< ! - - And that ' s it for the search widget -->
2022-03-30 18:12:06 +02:00
"""
2024-04-29 18:17:57 +02:00
2022-03-30 18:12:06 +02:00
return page
2024-04-29 18:17:57 +02:00
def source_code_link ( page ) :
2022-03-30 18:12:06 +02:00
# Adds a source code link
page = page + """
2024-04-29 18:17:57 +02:00
< ! - - This is the footer of every page - - >
< footer >
< div class = " container " >
< p > This website is under the GNU AGPL license . < / p >
< div class = " button-group " >
< a class = " button " href = " / " title = " Come back to the home page. " > Home < / a >
< a class = " button " href = " https://notabug.org/jyamihud/FreeCompetitors " title = " See the full source code of the software that powers this website. " > Source Code < / a >
< a class = " button " href = " /faq " title = " Frequently Asked Questions " > FAQ < / a >
< a class = " button " href = " https://notabug.org/jyamihud/FreeCompetitors/issues " title = " Report a Bug. " > Bug ? < / a >
< a class = " button " href = " https://notabug.org/jyamihud/FreeCompetitors/issues/25 " title = " Report a piece of software that ' s missing from the catalogue. " > Missing ? < / a >
< a class = " button " href = " https://notabug.org/jyamihud/FreeCompetitors/issues/24 " title = " Report a mistake in data about software. " > Mistake ? < / a >
< a class = " button " href = " /stats " title = " See stats of this server. " > Stats < / a >
< / div >
< / div >
< / footer >
< ! - - And this was the page of Free Competitors . No JavaScript .
2022-04-14 21:30:22 +02:00
No crap . No trackers . No nothing . And still works . Take that
Google ! ! ! - - >
2024-04-29 18:17:57 +02:00
"""
2022-04-16 08:30:50 +02:00
return page
2024-04-29 18:17:57 +02:00
def progress ( page , frac , text = " " ) :
2022-04-16 08:30:50 +02:00
# This will output a progress bar
page = page + """
2022-04-16 12:37:13 +02:00
< ! - - == == == == == == PROGRESS BARS == == == == == == =
Note that the progress bars will be visible only
if the CSS file is configured for the divs of
the progress bars . Otherwise it will just show
the text . - - >
2022-04-16 08:30:50 +02:00
< div class = " back_progress " >
< div class = " front_progress " style = " width: " " " + str ( frac * 100 ) + """ % " >
< / div >
< / div >
& nbsp ; & nbsp ; """ +str(text)+ """
"""
2022-03-29 14:44:03 +02:00
return page
2022-04-16 12:37:13 +02:00
2024-04-29 18:17:57 +02:00
def stats ( page , mis = False , data = [ ] ) :
2022-04-16 12:37:13 +02:00
# Graph will require some math to work.
# We are going to use CSS and HTML5 to
# draw it / interact with it.
2024-04-29 18:17:57 +02:00
spd = 60 * 60 * 24 # Seconds in each day
span = max ( data ) - min ( data ) # Time of seconds between first and last entry
days = span / spd # Time in days between first and last entry
2022-04-16 12:37:13 +02:00
# making sure there is at least something to show
if days < 20 :
2024-04-29 18:17:57 +02:00
data . append ( min ( data ) - ( spd * 20 ) )
spd = 60 * 60 * 24
span = max ( data ) - min ( data )
days = span / spd
2022-04-16 12:37:13 +02:00
2024-04-29 18:17:57 +02:00
widthfrac = 100 / days # the % to use for the width of each div
2022-04-16 12:37:13 +02:00
# Count how much hits happen per each day
biggest = 0
day_counts = [ ]
for d in range ( int ( round ( days ) ) ) :
count = 0
for i in data :
2024-04-29 18:17:57 +02:00
day = min ( data ) + ( d * spd )
if int ( i ) in range ( int ( day ) , int ( day + spd ) ) :
2022-04-16 12:37:13 +02:00
count + = 1
if count > biggest :
biggest = count
day_counts . append ( count )
# Render them
2024-04-29 18:17:57 +02:00
page = page + " <center> "
2022-04-16 12:37:13 +02:00
for n , d in enumerate ( day_counts ) :
if n == 0 :
d - = 1
2024-04-29 18:17:57 +02:00
dayis = min ( data ) + ( n * spd )
dayis = time . strftime ( " % Y/ % m/ %d " , time . gmtime ( dayis ) )
page = page + ' <div class= " front-progress " title= " ' + str ( dayis ) + ' : ' + str (
d ) + ' visitors " style= " height:400; position:absolute; top:5 % ; left:calc(99vw/ ' + str (
days ) + ' * ' + str ( n ) + ' ) ;width: ' + str ( widthfrac ) + ' % " > \n '
frac = 100 - ( d / biggest * 99 )
page = page + ' <div class= " back-progress " style= " height: ' + str ( frac ) + ' % ; bottom: ' + str (
frac ) + ' % " ></div></div> \n \n '
2022-04-16 12:37:13 +02:00
page = page + ' \n </center> \n \n <div style= " position:absolute; top:500 " > '
# If to render missing software
2023-03-18 14:25:41 +01:00
# TODO: We had a list of missing software which are known of,
# but people abused this feature to add a bunch of hilarious spam.
# maybe there is a way to design this better.
2022-04-16 12:37:13 +02:00
2024-04-29 18:17:57 +02:00
# if mis:
# page = page + missing.List_html()
2022-04-16 12:37:13 +02:00
page = source_code_link ( page )
2024-04-29 18:17:57 +02:00
return page