render.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. # THIS SOFTWARE IS A PART OF FREE COMPETITOR PROJECT
  2. # THE FOLLOWING SOURCE CODE I UNDER THE GNU
  3. # AGPL LICENSE V3 OR ANY LATER VERSION.
  4. # This project is not for simple users, but for
  5. # web-masters and a like, so we are counting on
  6. # your ability to set it up and running.
  7. import os
  8. import time
  9. from modules import search
  10. from modules import missing
  11. def html(page, json):
  12. # This function adds a rendering of the json into the page
  13. free = search.is_free(json)
  14. name = json.get("names",["Unknown"])[0]
  15. page = page + """
  16. <!-- For each software in the list we do mostly the same things.
  17. And here it is. First we show it's name as a link to be able
  18. to search Free Competitors to what is found. -->
  19. """
  20. page = page + "\n <a href=\"/"+name.replace(" ", "+").lower()+"\">"
  21. page = page + "\n <h1>"
  22. try:
  23. page = page + '\n <img src="'+ json["links"]["icon"] + '" alt="[LOGO]" style="height:50px;"> <!-- The logo of the software in question.-->\n'
  24. except:
  25. pass
  26. page = page + " "+ name +" <!-- The title of the program -->\n"
  27. page = page + " </h1>\n </a>\n\n"
  28. page = page + """
  29. <!-- Next there is a short paragraph about the program.
  30. ( Often Copypasted from some official page about it ).
  31. Which means, it may contain the terrible words "Open Source"
  32. that hide the fact that Free Software is about User Freedom
  33. first of all. So we are trying to link to a special page on
  34. GNU.ORG if such a thing is found -->
  35. """
  36. # Few words about it
  37. comment = json.get("comment","")
  38. not_foss = ['open source', 'open-source']
  39. if "open source" or "open-source" in comment.lower():
  40. # Well... Here is a thing. Free Software, not open source.
  41. where = comment.lower().find("open source")
  42. # In case it has a slash in it.
  43. if where == -1:
  44. where = comment.lower().find("open-source")
  45. ops = comment[where:where+11]
  46. if ops:
  47. comment = comment.replace(ops,
  48. "<a href=\"https://www.gnu.org/philosophy/open-source-misses-the-point.en.html\">"+ops+"</a>")
  49. page = page + "\n <p>\n "+comment+"\n </p>\n\n"
  50. # I want to show nothing else from if it's proprietary
  51. issues_files = list(os.listdir("data/issues"))
  52. if "issues" in json and json["issues"]:
  53. l = json.get("issues", [])
  54. page = page +"<h2>Anti-Features / Problems:</h2>"
  55. for i in l:
  56. if i+".html" not in issues_files:
  57. page = page + "&nbsp;&nbsp;"+i+"<br>"
  58. else:
  59. page = page + '<details title="Read about '+i+'">'
  60. page = page + "<summary>&nbsp;&nbsp"+i+"</summary>"
  61. issuefile = open("data/issues/"+i+".html")
  62. page = page + "<span><p>"+issuefile.read()+"</p></span>"
  63. page = page + "</details>"
  64. if not free:
  65. return page
  66. # Links
  67. page = page + """
  68. <!-- And now the table itself. -->
  69. """
  70. linksfilter = {"git":"source code","fsd":"Free Software Directory"}
  71. links = json.get("links", {})
  72. for website in links:
  73. if website in ["icon"]:
  74. continue
  75. link = links[website]
  76. page = page + """
  77. <!-- Here's how to do a simple button -->
  78. <a class="button" href=\""""+link+"""\" title=\""""+link+"""\">"""+linksfilter.get(website,website).upper()+"""</a>
  79. """
  80. page = page + """
  81. <!-- Details are those little collapsable things that I like to
  82. use very much. It's very simple really. Just read the code
  83. carefully and you will get it -->
  84. """
  85. # Details
  86. categories = {"generic_name":"Features",
  87. "licenses":"License(s)",
  88. "platforms":"Platforms",
  89. "networks_read":"Accesses Data from",
  90. "networks_write":"Interacts / Publishes to",
  91. "formats_read":"Opens from File-Formats",
  92. "formats_write":"Saves to File-Formats",
  93. "interface":"Interface",
  94. "languages":"Programming Languages"}
  95. for c in categories:
  96. l = json.get(c, [])
  97. if not l:
  98. continue
  99. # I want to look whether this category has a list of files
  100. alldata = list(os.listdir("data"))
  101. allfiles = []
  102. for folder in alldata:
  103. if c.startswith(folder):
  104. try:
  105. allfiles = list(os.listdir("data/"+folder))
  106. break
  107. except:
  108. pass
  109. # Count matches
  110. matches = 0
  111. for i in l:
  112. if i.startswith("*"):
  113. matches += 1
  114. if matches:
  115. matchtext = "<i>( "+str(matches)+" )</i>"
  116. else:
  117. matchtext = ""
  118. page = page + "\n\n <details>"
  119. page = page +"\n <summary>"+categories[c]+": "+matchtext+"</summary>"
  120. for i in l:
  121. matchtext = ""
  122. if i.startswith("*"):
  123. i = i[1:]
  124. matchtext = "&nbsp;&nbsp;&nbsp;&nbsp;<i>( match )</i> "
  125. if i+".html" in allfiles:
  126. datapage = open("data/"+folder+"/"+i+".html")
  127. page = page + """
  128. <!-- Just so happened that about \""""+i+"""\" we had a file
  129. in data/"""+folder+""" folder. So why not make a detail inside a detail,
  130. a detail-sception, so to speak. And add the text of explanation into it. -->
  131. """
  132. page = page + "<details>\n"
  133. page = page + " <summary>&nbsp;&nbsp;&nbsp;&nbsp;"+matchtext+i+"</summary>\n"
  134. page = page + " <span>\n <p>\n"
  135. page = page + " " + datapage.read()+"\n"
  136. page = page + " </p>\n </span>\n </details>"
  137. else:
  138. page = page + "\n <span>&nbsp;&nbsp;&nbsp;&nbsp;"+matchtext+i+"</span>\n <br>\n"
  139. page = page + """
  140. <!-- Just a tiny space after all the spans. So no to feel
  141. too crowded, so to speak -->
  142. <span><br></span>
  143. </details>
  144. """
  145. return page
  146. def suggestions(page, json):
  147. # This function will render suggestions
  148. page = page + """
  149. <!-- ===========================================================
  150. This is where ther actual competitors are starting to show!!!
  151. ============================================================ -->
  152. <h1>Free Competitors:</h1>
  153. """
  154. found = search.suggest(json)
  155. biggest = 0
  156. for i in found:
  157. if i[0] > biggest:
  158. biggest = i[0]
  159. more = False
  160. for i in found:
  161. free = search.is_free(i[-1])
  162. if not i[0] or i[-1]["names"] == json["names"] or not free:
  163. continue
  164. try:
  165. frac = int(i[0]/biggest*100)
  166. except:
  167. frac = 0
  168. if frac < 10 and not more: # Below 10% features match
  169. page = page + """
  170. <!-- Sometimes the suggestion is not very good. Below 10%
  171. of suggestion score. But it still kind of valid. So we
  172. want to put it into the page. Only when the user clicks
  173. something. Why not using the same old details? -->
  174. <hr>
  175. <details>
  176. <summary><h1 title="Click to show more / less.">Problematic Competitors:</h1></summary>
  177. """
  178. more = True
  179. page = page +"""
  180. <br><br>
  181. <!-- ================================================================== -->
  182. """
  183. page = progress(page, frac/100, "Suggestion score: " + str(frac) + "%")
  184. page = html(page, i[-1])
  185. if more:
  186. page = page + "</details>"
  187. return page
  188. def search_widget(page, address):
  189. # Adds a search bar to the page
  190. page = page + """
  191. <!-- Search widget! This widget makes it possible to implement
  192. a search feature without using a single line of JavaScript code.
  193. In HTML, there is an input field that we can use. If we pare it
  194. with a button into a <form>, we can get a button that activates
  195. the search. -->
  196. <form action="""
  197. page = page + address
  198. page = page + """search method="GET">
  199. <input type="text" name="item" class="search" placeholder="Name of Software">
  200. <button type="submit">Search</button>
  201. </form>
  202. <!-- And that's it for the search widget -->
  203. """
  204. #page = page.format(ADDRESS)
  205. return page
  206. def source_code_link(page):
  207. # Adds a source code link
  208. page = page + """
  209. <!-- This the the footer of every page -->
  210. <hr>
  211. This website is under the GNU AGPL license.
  212. <!-- As always I want to add a bit of CSS to make tables
  213. invisible -->
  214. <br><br>
  215. <a class="button" href="/" title="Come back to the home page.">HOME</a>
  216. <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>
  217. <a class="button" href="/faq" title="Frequently Asked Questions">FAQ</a>
  218. <a class="button" href="https://notabug.org/jyamihud/FreeCompetitors/issues" title="Report a Bug.">BUG?</a>
  219. <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>
  220. <a class="button" href="https://notabug.org/jyamihud/FreeCompetitors/issues/24" title="Report a mistake in data about software.">MISTAKE?</a>
  221. <a class="button" href="/stats" title="See stats of this server.">STATS</a>
  222. <!-- And this was the page of Free Competitors. No Javascript.
  223. No crap. No trackers. No nothing. And still works. Take that
  224. Google!!! -->
  225. """
  226. return page
  227. def progress(page, frac, text=""):
  228. # This will output a progress bar
  229. page = page + """
  230. <!-- ============ PROGRESS BARS =============
  231. Note that the progress bars will be visible only
  232. if the CSS file is configured for the divs of
  233. the progress bars. Otherwise it will just show
  234. the text. -->
  235. <div class="back_progress">
  236. <div class="front_progress" style="width:"""+str(frac*100)+"""%">
  237. </div>
  238. </div>
  239. &nbsp;&nbsp;"""+str(text)+"""
  240. """
  241. return page
  242. def stats(page, mis=False, data=[]):
  243. # Graph will require some math to work.
  244. # We are going to use CSS and HTML5 to
  245. # draw it / interact with it.
  246. spd = 60*60*24 # Seconds in each day
  247. span = max(data) - min(data) # Time of seconds between first and last entry
  248. days = span / spd # Time in days between first and last entry
  249. # making sure there is at least something to show
  250. if days < 20:
  251. data.append(min(data)-(spd*20))
  252. spd = 60*60*24
  253. span = max(data) - min(data)
  254. days = span / spd
  255. widthfrac = 100 / days # the % to use for the width of each div
  256. # Count how much hits happen per each day
  257. biggest = 0
  258. day_counts = []
  259. for d in range(int(round(days))):
  260. count = 0
  261. for i in data:
  262. day = min(data) + (d*spd)
  263. if int(i) in range(int(day), int(day+spd)):
  264. count += 1
  265. if count > biggest:
  266. biggest = count
  267. day_counts.append(count)
  268. # Render them
  269. page = page +"<center>"
  270. for n, d in enumerate(day_counts):
  271. if n == 0:
  272. d -= 1
  273. dayis = min(data)+(n*spd)
  274. dayis = time.strftime("%Y/%m/%d", time.gmtime( dayis) )
  275. 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'
  276. frac = 100-(d/biggest*99)
  277. page = page + '<div class="back_progress" style="height:'+str(frac)+'%; bottom:'+str(frac)+'%"></div></div>\n\n'
  278. page = page + '\n</center>\n\n<div style="position:absolute; top:500">'
  279. # If to render missing software
  280. # TODO: We had a list of missing software which are known of,
  281. # but people abused this feature to add a bunch of hilarious spam.
  282. # maybe there is a way to design this better.
  283. #if mis:
  284. # page = page + missing.List_html()
  285. page = source_code_link(page)
  286. return page