Added a graph of general usage
This commit is contained in:
parent
079c922b73
commit
326347421b
6 changed files with 221 additions and 33 deletions
|
@ -31,5 +31,33 @@
|
||||||
1650090012.933084,
|
1650090012.933084,
|
||||||
1650090481.4744847,
|
1650090481.4744847,
|
||||||
1650090521.486587,
|
1650090521.486587,
|
||||||
1650090584.648241
|
1650090584.648241,
|
||||||
|
1650095974.4940188,
|
||||||
|
1650097447.4327528,
|
||||||
|
1650100147.3385897,
|
||||||
|
1650100566.5777473,
|
||||||
|
1650102075.2005043,
|
||||||
|
1650103439.1721528,
|
||||||
|
1650103628.453873,
|
||||||
|
1650103669.108851,
|
||||||
|
1650103715.6338923,
|
||||||
|
1650103792.0688775,
|
||||||
|
1650103848.74207,
|
||||||
|
1650103929.5856616,
|
||||||
|
1650104068.4825075,
|
||||||
|
1650104211.148559,
|
||||||
|
1650104242.4403458,
|
||||||
|
1650104410.298269,
|
||||||
|
1650104561.913953,
|
||||||
|
1650104761.2695749,
|
||||||
|
1650104807.013614,
|
||||||
|
1650104867.3241987,
|
||||||
|
1650105102.2744603,
|
||||||
|
1650105197.1579733,
|
||||||
|
1650105202.8585582,
|
||||||
|
1650105210.2589703,
|
||||||
|
1650105235.6373727,
|
||||||
|
1650105264.9144416,
|
||||||
|
1650105289.841991,
|
||||||
|
1650105298.780408
|
||||||
]
|
]
|
16
default.css
16
default.css
|
@ -137,6 +137,11 @@ p {
|
||||||
}
|
}
|
||||||
|
|
||||||
.side_found { margin-top: 100}
|
.side_found { margin-top: 100}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If a screen is at least 1024 pixels wide, we do some more things.
|
||||||
|
*/
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1024px) {
|
||||||
.recomendations {
|
.recomendations {
|
||||||
margin-left: 40%;}
|
margin-left: 40%;}
|
||||||
|
@ -151,4 +156,15 @@ p {
|
||||||
top:100;
|
top:100;
|
||||||
left:5%;} }
|
left:5%;} }
|
||||||
|
|
||||||
|
/* prgoress bar theme */
|
||||||
|
|
||||||
|
.back_progress {background-color: #543ba3;
|
||||||
|
width: 100%;
|
||||||
|
height: 7}
|
||||||
|
.back_progress:hover {
|
||||||
|
background-color: #CC35F0;
|
||||||
|
}
|
||||||
|
.front_progress {background-color: #96bbe8;
|
||||||
|
height: 7}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
41
faq.html
41
faq.html
|
@ -1,17 +1,4 @@
|
||||||
<style>
|
<!-- Frequently asked questions file is 'faq.html' . See Sources. -->
|
||||||
|
|
||||||
.test {
|
|
||||||
width: 30%;
|
|
||||||
height: 30%;
|
|
||||||
background: #928374;
|
|
||||||
position: fixed;
|
|
||||||
top: 5%;
|
|
||||||
right: 5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
<div class="test">Some test text</div>
|
|
||||||
|
|
||||||
|
|
||||||
<h1>Frequently Asked Questions:</h1>
|
<h1>Frequently Asked Questions:</h1>
|
||||||
|
|
||||||
|
@ -73,6 +60,30 @@
|
||||||
</p></span>
|
</p></span>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Does this website uses JavaScript?</summary>
|
||||||
|
<span><p>
|
||||||
|
|
||||||
|
No. We do not use a single line of JavaScript. Now, you are probably wondering how we make so many cool things with the website. Like progress bars and graphs? Well, we are using CSS for it. And CSS is really all you need to render pretty websites. JavaScript is really unnecessary. You can right-click and see sources of the pages. They have comments.
|
||||||
|
|
||||||
|
</p></span>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Does this website uses cookies or other trackers?</summary>
|
||||||
|
<span><p>
|
||||||
|
|
||||||
|
We do not use cookies. We respect the privacy of every person. Furthermore this website is designed to work over Tor. If you are connecting via .onion domain, we do not even see what Tor node is connecting. For the server, it seems like it's connecting to itself. So, if you using .onion site, we don't have anyway to even tell whether you connected to it before. Or how many times you connected to it. At a non .onion instance, your IP address will be visible to the website operator. The software is not configured to record them. But you have to trust the website operator. So we are advising to still use .onion version, or simply connect over Tor or VPN ( to anything ).</p>
|
||||||
|
|
||||||
|
<p>We do however see what is being searched. What pages are requested to be served and how often they are requested. For example, if you are requesting to see the Free Competitors for let's say <a href="/whatsapp">WhatsApp</a> it will request the server to render and send back the WhatsApp page. We can see when the server does it and what page is requested.</p>
|
||||||
|
|
||||||
|
<p>This fact lets us generate truly anonymous analytics. For example, we can detect what software is being searched, but not yet added into our database. So to create a list for us on what to work on.</p>
|
||||||
|
|
||||||
|
<p>One more thing that we can use it for. Is to get a <a href="/stats">general usage analytics</a>. Without counties and people's names. Just the amounts and times people used the site. For example, each time you load the web-site for the first time ( on each session of your browser ) it will request the favicon. A little logo to put in the corner of the tab. It does it only once per session ( usually ). So we can record the times when the favicon was requested to get a general idea of how many users use the site. Without knowing who they are, or anything about them.
|
||||||
|
|
||||||
|
</p></span>
|
||||||
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>How does it find Competitors?</summary>
|
<summary>How does it find Competitors?</summary>
|
||||||
<span><p>
|
<span><p>
|
||||||
|
@ -134,3 +145,5 @@
|
||||||
<br><br>
|
<br><br>
|
||||||
Now search for something and get a list of Free Competitors for it.
|
Now search for something and get a list of Free Competitors for it.
|
||||||
|
|
||||||
|
<!-- After the document, the server software will attach the search and
|
||||||
|
footer. So there is no need to put it into the 'faq.html' document.-->
|
||||||
|
|
|
@ -161,3 +161,51 @@ def List():
|
||||||
else:
|
else:
|
||||||
s = s + " | |"
|
s = s + " | |"
|
||||||
print(s)
|
print(s)
|
||||||
|
|
||||||
|
|
||||||
|
def List_html():
|
||||||
|
|
||||||
|
# This function will list missing in html format
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("data/missing.json") as json_file:
|
||||||
|
missing = json.load(json_file)
|
||||||
|
|
||||||
|
# Reverse the old file
|
||||||
|
if type(missing) == dict:
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
except:
|
||||||
|
missing = []
|
||||||
|
|
||||||
|
page = """
|
||||||
|
|
||||||
|
<h1>Known Missing Software</h1>
|
||||||
|
|
||||||
|
<p>This list is auto-generated by the server software when the software
|
||||||
|
cannot find a '.json' for for the searched query. It may or may not
|
||||||
|
be know to the developers, or even to the server operators. So please report
|
||||||
|
the software into <a href="https://notabug.org/jyamihud/FreeCompetitors/issues/25">
|
||||||
|
the Missing Software Page</a> in our source code repository.</p>
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
for i in missing:
|
||||||
|
i = sorted(i.items(), key=lambda x:x[1])
|
||||||
|
i = dict(i)
|
||||||
|
|
||||||
|
if len(i) > 1:
|
||||||
|
page = page + "<details><summary>"+list(i.keys())[0]+"</summary>"
|
||||||
|
for b in i:
|
||||||
|
if b == list(i.keys())[0]:
|
||||||
|
continue
|
||||||
|
if b == list(i.keys())[-1]:
|
||||||
|
comma = ""
|
||||||
|
page = page + "<span> " + b + "</span><br>"
|
||||||
|
page = page + "</details>"
|
||||||
|
else:
|
||||||
|
page = page + list(i.keys())[0]+"<br>"
|
||||||
|
page = page + "\n\n"
|
||||||
|
|
||||||
|
return page
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
# your ability to set it up and running.
|
# your ability to set it up and running.
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
from modules import search
|
from modules import search
|
||||||
|
from modules import missing
|
||||||
|
|
||||||
|
|
||||||
def html(page, json):
|
def html(page, json):
|
||||||
|
@ -318,7 +320,11 @@ def source_code_link(page):
|
||||||
<!-- This is self explanatory ( if you read most of the page ) -->
|
<!-- This is self explanatory ( if you read most of the page ) -->
|
||||||
|
|
||||||
<table><tr>
|
<table><tr>
|
||||||
|
|
||||||
|
<th><form action=/>
|
||||||
|
<button title="Come back to the home page." type="submit">HOME</button>
|
||||||
|
</form></th>
|
||||||
|
|
||||||
<th><form action=https://notabug.org/jyamihud/FreeCompetitors>
|
<th><form action=https://notabug.org/jyamihud/FreeCompetitors>
|
||||||
<button title="See the full source code of the software that powers this website." type="submit">SOURCE</button>
|
<button title="See the full source code of the software that powers this website." type="submit">SOURCE</button>
|
||||||
</form></th>
|
</form></th>
|
||||||
|
@ -337,6 +343,10 @@ def source_code_link(page):
|
||||||
|
|
||||||
<th><form action=https://notabug.org/jyamihud/FreeCompetitors/issues/24>
|
<th><form action=https://notabug.org/jyamihud/FreeCompetitors/issues/24>
|
||||||
<button title="Report a mistake in data about software." type="submit">MISTAKE?</button>
|
<button title="Report a mistake in data about software." type="submit">MISTAKE?</button>
|
||||||
|
</form></th>
|
||||||
|
|
||||||
|
<th><form action=/stats>
|
||||||
|
<button title="See stats of this server." type="submit">STATS</button>
|
||||||
</form></th></tr>
|
</form></th></tr>
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
@ -358,19 +368,11 @@ def progress(page, frac, text=""):
|
||||||
|
|
||||||
page = page + """
|
page = page + """
|
||||||
|
|
||||||
<!-- In order for this to work with any CSS file, the
|
<!-- ============ PROGRESS BARS =============
|
||||||
style of the little progress bar should be defined
|
Note that the progress bars will be visible only
|
||||||
inline. -->
|
if the CSS file is configured for the divs of
|
||||||
|
the progress bars. Otherwise it will just show
|
||||||
<style>
|
the text. -->
|
||||||
.back_progress {background-color: #543ba3;
|
|
||||||
width: 100%;
|
|
||||||
height: 7}
|
|
||||||
.front_progress {background-color: #96bbe8;
|
|
||||||
height: 7}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Then we are going to use two divs -->
|
|
||||||
|
|
||||||
<div class="back_progress">
|
<div class="back_progress">
|
||||||
<div class="front_progress" style="width:"""+str(frac*100)+"""%">
|
<div class="front_progress" style="width:"""+str(frac*100)+"""%">
|
||||||
|
@ -379,3 +381,62 @@ def progress(page, frac, text=""):
|
||||||
"""+str(text)+"""
|
"""+str(text)+"""
|
||||||
"""
|
"""
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
def stats(page, mis=False, data=[]):
|
||||||
|
|
||||||
|
|
||||||
|
# Graph will require some math to work.
|
||||||
|
# We are going to use CSS and HTML5 to
|
||||||
|
# draw it / interact with it.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# making sure there is at least something to show
|
||||||
|
if days < 20:
|
||||||
|
data.append(min(data)-(spd*20))
|
||||||
|
spd = 60*60*24
|
||||||
|
span = max(data) - min(data)
|
||||||
|
days = span / spd
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
widthfrac = 100 / days # the % to use for the width of each div
|
||||||
|
|
||||||
|
# Count how much hits happen per each day
|
||||||
|
biggest = 0
|
||||||
|
day_counts = []
|
||||||
|
print(days)
|
||||||
|
for d in range(int(round(days))):
|
||||||
|
count = 0
|
||||||
|
for i in data:
|
||||||
|
day = min(data) + (d*spd)
|
||||||
|
if int(i) in range(int(day), int(day+spd)):
|
||||||
|
count += 1
|
||||||
|
if count > biggest:
|
||||||
|
biggest = count
|
||||||
|
day_counts.append(count)
|
||||||
|
|
||||||
|
# Render them
|
||||||
|
page = page +"<center>"
|
||||||
|
for n, d in enumerate(day_counts):
|
||||||
|
if n == 0:
|
||||||
|
d -= 1
|
||||||
|
|
||||||
|
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*100)
|
||||||
|
page = page + '<div class="back_progress" style="height:'+str(frac)+'%; bottom:'+str(frac)+'%"></div></div>\n\n'
|
||||||
|
|
||||||
|
page = page + '\n</center>\n\n<div style="position:absolute; top:500">'
|
||||||
|
# If to render missing software
|
||||||
|
|
||||||
|
if mis:
|
||||||
|
page = page + missing.List_html()
|
||||||
|
|
||||||
|
|
||||||
|
page = source_code_link(page)
|
||||||
|
return page
|
||||||
|
|
30
server.py
30
server.py
|
@ -92,6 +92,12 @@ class handler(BaseHTTPRequestHandler):
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
def send(self, textin):
|
def send(self, textin):
|
||||||
|
|
||||||
|
software = self.path.replace("/", "").replace("+", " ")
|
||||||
|
|
||||||
|
if software:
|
||||||
|
software = ": "+software[0].upper()+software[1:].lower()
|
||||||
|
|
||||||
|
|
||||||
textin = str(textin)
|
textin = str(textin)
|
||||||
csstext = '<link media="all" href="'+CSS+'" type="text/css" rel="stylesheet" />'
|
csstext = '<link media="all" href="'+CSS+'" type="text/css" rel="stylesheet" />'
|
||||||
|
@ -99,7 +105,9 @@ class handler(BaseHTTPRequestHandler):
|
||||||
text = text + "<!-- Let's add some CSS, you can edit 'config.json' to change it. -->\n"
|
text = text + "<!-- Let's add some CSS, you can edit 'config.json' to change it. -->\n"
|
||||||
text = text + '<head>'+csstext+'\n\n'
|
text = text + '<head>'+csstext+'\n\n'
|
||||||
text = text + "<!-- Now we want the favicon to be PNG instead of ICO -->\n\n"
|
text = text + "<!-- Now we want the favicon to be PNG instead of ICO -->\n\n"
|
||||||
text = text + '<link rel="icon" href="favicon.png"></head>\n\n'
|
text = text + '<link rel="icon" href="favicon.png">\n\n'
|
||||||
|
text = text + "<!-- Now the title -->\n\n"
|
||||||
|
text = text + '<title>Free Competitors'+software+'</title></head>\n\n'
|
||||||
text = text + "<!-- Now the body. The main part of the page, so to speak. -->\n"
|
text = text + "<!-- Now the body. The main part of the page, so to speak. -->\n"
|
||||||
text = text + '<body>\n\n'+textin+'\n\n</body>'
|
text = text + '<body>\n\n'+textin+'\n\n</body>'
|
||||||
|
|
||||||
|
@ -145,9 +153,9 @@ class handler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
# The css file
|
# The css file
|
||||||
|
|
||||||
cssfile = open("default.css")
|
cssfile = open("default.css", "rb")
|
||||||
cssfile = cssfile.read()
|
cssfile = cssfile.read()
|
||||||
self.send(cssfile)
|
self.wfile.write(cssfile)
|
||||||
|
|
||||||
elif self.path == "/font":
|
elif self.path == "/font":
|
||||||
|
|
||||||
|
@ -155,7 +163,7 @@ class handler(BaseHTTPRequestHandler):
|
||||||
|
|
||||||
fontfile = open("OpenSans-ExtraBold.ttf", "rb")
|
fontfile = open("OpenSans-ExtraBold.ttf", "rb")
|
||||||
fontfile = fontfile.read()
|
fontfile = fontfile.read()
|
||||||
self.send(fontfile)
|
self.wfile.write(fontfile)
|
||||||
|
|
||||||
elif self.path in ["/faq", "/faq?"]:
|
elif self.path in ["/faq", "/faq?"]:
|
||||||
|
|
||||||
|
@ -194,6 +202,20 @@ class handler(BaseHTTPRequestHandler):
|
||||||
icon = open("favicon.png", "rb")
|
icon = open("favicon.png", "rb")
|
||||||
icon = icon.read()
|
icon = icon.read()
|
||||||
self.wfile.write(icon)
|
self.wfile.write(icon)
|
||||||
|
|
||||||
|
elif self.path in ["/stats", "/stats?"]:
|
||||||
|
|
||||||
|
# Analytics / statistics availble for all users of
|
||||||
|
# the website.
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("data/favicon_requests.json") as json_file:
|
||||||
|
favicons = json.load(json_file)
|
||||||
|
except:
|
||||||
|
favicons = []
|
||||||
|
|
||||||
|
page = render.stats("", True, favicons)
|
||||||
|
self.send(page)
|
||||||
|
|
||||||
elif "/search?item=" in self.path:
|
elif "/search?item=" in self.path:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue