Переглянути джерело

samples and time/duration refactor

Johann Woelper 7 роки тому
батько
коміт
4aba6a3a9c
4 змінених файлів з 84 додано та 29 видалено
  1. 9 0
      app.js
  2. 8 1
      test.html
  3. 57 28
      src/main.rs
  4. 10 0
      urls.json

+ 9 - 0
app.js

@@ -1,5 +1,14 @@
 Number.prototype.currency = function() { return '$' + this.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,') };
 
+function seconds_to_time(s) {
+    var duration = moment.duration(s, 'seconds');
+    return duration.format("DD-hh:mm");
+}
+
+function ts_to_remaining(ts) {
+    return moment.unix(ts).fromNow();
+}
+
 
 function log() {
     console.log('LOG >>>', arguments);

+ 8 - 1
test.html

@@ -7,6 +7,8 @@
   <meta name="theme-color" content="#dddddd">
   <link rel="icon" sizes="128x128" href="icon.png">
   
+
+
   <style>
     * {
       margin: 0;
@@ -58,7 +60,7 @@
             <div>
               <div class="badge">{{item.price}} EUR</div>
             </div>
-            <div>Noch {{item.remaining}}</div>
+            <div>{{ts_to_remaining(item.timestamp)}}</div>
       <a v-bind:href="item.gcal">Add to cal</a>
 	  </div>
         </div>
@@ -71,6 +73,11 @@
   </div>
 </body>
 
+<!-- Moment.js library -->
+<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
+
+<!-- moment-duration-format plugin -->
+<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-duration-format/1.3.0/moment-duration-format.min.js"></script>
 
 <script defer src="https://cdn.jsdelivr.net/npm/vue@2.5.13/dist/vue.min.js"></script>
 <link rel="stylesheet" href="https://rawgit.com/woelper/lilac/master/lilac.min.css">

+ 57 - 28
src/main.rs

@@ -5,23 +5,22 @@ use chrono::{Utc, Duration, Local, DateTime};
 use select::document::Document;
 use select::predicate::Name;
 use parse_duration::parse;
-use serde;
 use serde_json;
 use std::io::BufWriter;
 use std::io::BufReader;
 use std::fs::File;
 
-/// Fucking egun is a mess. It does not even have classes. This is an attempt to parse it.
+/// Fucking egun is a mess. It does not even use css and is built using tables. This is an attempt to parse it.
 
 
 
-#[derive(Serialize, Deserialize, Debug, Default)]
+#[derive(Serialize, Deserialize, Debug, Default, Clone)]
 struct Auction {
     price: f32,
     desc: String,
     gcal: String,
     thumb: String,
-    remaining: String,
+    remaining: i64,
     url: String,
     timestamp: i64,
     is_price_final: bool
@@ -31,7 +30,7 @@ struct Auction {
 struct Query {
     url: String,
     auctions: Vec<Auction>,
-    frequency: i64
+    frequency: i64,
 }
 
 
@@ -39,6 +38,13 @@ impl Query {
     fn run(&mut self) {
         self.auctions = parse_url(&self.url);
     }
+
+    fn detect_frequency(&self) {
+        let _a = &self.auctions
+        .iter()
+        .map(|x| x)
+        .collect::<Vec<_>>();
+    }
 }
 
 impl Default for Query {
@@ -67,12 +73,20 @@ fn date_to_gcal(date: DateTime<Local>) -> String {
 }
 
 /// Parse a relative offset "x Tage HH:MM" into proper time
-fn parse_end_date(timestring: &String) -> Option<DateTime<Local>> {
+// fn parse_end_date(timestring: &String) -> Option<DateTime<Local>> {
+//     match parse(&format!("{} minutes", timestring.replace(":", " hours ").replace("Tage", "days"))).ok() {
+//         Some(old_duration) => match Duration::from_std(old_duration).ok() {
+//             Some(chronoduration) => Some(Utc::now().with_timezone(&Local) + chronoduration),
+//             None => None
+//         }
+//         None => None
+//     }
+// }
+
+/// Parse a relative offset "x Tage HH:MM" into proper time
+fn parse_remaining(timestring: &String) -> Option<Duration> {
     match parse(&format!("{} minutes", timestring.replace(":", " hours ").replace("Tage", "days"))).ok() {
-        Some(old_duration) => match Duration::from_std(old_duration).ok() {
-            Some(chronoduration) => Some(Utc::now().with_timezone(&Local) + chronoduration),
-            None => None
-        }
+        Some(old_duration) => Duration::from_std(old_duration).ok(),
         None => None
     }
 }
@@ -87,13 +101,15 @@ fn parse_price(price: &str) -> Option<f32> {
 
 
 fn parse_url(url: &str) -> Vec<Auction> {
-    let mut resp = reqwest::get(url).unwrap();
-    assert!(resp.status().is_success());
-    // dbg!(&resp.text());
+    let mut auctions = vec![];
 
-    let text = resp.text().unwrap();
+    if let Ok(mut resp) = reqwest::get(url) {
+        if !resp.status().is_success() {
+            return auctions;
+        }
+
+    let text = resp.text().unwrap_or("".to_string());
 
-    let mut auctions = vec![];
 
 
     for node in Document::from_read(text.as_bytes())
@@ -123,6 +139,8 @@ fn parse_url(url: &str) -> Vec<Auction> {
                     // instantiate mutable Auction
                     let mut auction = Auction::default();
                     auction.desc = name.clone();
+                    auction.thumb = format!("http://egun.de/market/images/picture.gif");
+
 
                     // get image                
                     if let Some(img) = node
@@ -154,7 +172,6 @@ fn parse_url(url: &str) -> Vec<Auction> {
                     }
 
 
-                
                     // get article url
                     if let Some(url) = node
                     .children().into_iter()
@@ -169,7 +186,7 @@ fn parse_url(url: &str) -> Vec<Auction> {
                     }
                 
                     // TODO: check if https://doc.rust-lang.org/std/time/struct.SystemTime.html works too
-                    if let Some(remaining) = parse_end_date(
+                    if let Some(remaining) = parse_remaining(
                         &node
                         .children()
                         .filter(|x| x.attr("align") == Some("center"))
@@ -181,8 +198,11 @@ fn parse_url(url: &str) -> Vec<Auction> {
                         .join(" ")
                     ){
                         // dbg!(&t_remaining.children());
-                        auction.gcal = format!("http://www.google.com/calendar/event?action=TEMPLATE&dates={}&text={}&location=&details=", date_to_gcal(remaining), auction.desc);
-                        auction.timestamp = remaining.timestamp();
+
+                        let end_date = Utc::now().with_timezone(&Local) + remaining;
+                        auction.gcal = format!("http://www.google.com/calendar/event?action=TEMPLATE&dates={}&text={}&location=&details=", date_to_gcal(end_date), auction.desc);
+                        auction.remaining = remaining.num_seconds();
+                        auction.timestamp = end_date.timestamp();
                         // println!("ENDS\t{:?}", date_to_gcal(remaining));
 
                     }
@@ -191,25 +211,34 @@ fn parse_url(url: &str) -> Vec<Auction> {
                 }
         }
 
-        auctions
+    }
+    auctions
 }
 
 
 
 
 fn main() {
-    let url = "http://www.egun.de/market/list_items.php?mode=qry&plusdescr=off&wheremode=and&query=sniper&quick=1";
+
     
+    let mut auctions = vec![];
+
+    let reader = BufReader::new(File::open("urls.json").unwrap());
+    let urls: Vec<String> = serde_json::from_reader(reader).unwrap();
+
 
-    let mut query = Query {
-        url: url.to_string(),
-        ..Default::default()
-    };
+    for url in urls {
 
-    query.run();
-    dbg!(query);
+        let mut query = Query {
+            url: url.to_string(),
+            ..Default::default()
+        };
+        query.run();
+
+        auctions.extend(query.auctions);
+    }
 
     // write out the file
     let writer = BufWriter::new(File::create("db.json").unwrap());
-    serde_json::to_writer_pretty(writer, &parse_url(url)).unwrap();
+    serde_json::to_writer_pretty(writer, &auctions).unwrap();
 }

+ 10 - 0
urls.json

@@ -0,0 +1,10 @@
+[
+    "http://www.egun.de/market/list_items.php?mode=qry&query=m1a&plusdescr=off&wheremode=and&ewb=1&status=&minprice=&maxprice=1900&type=0&nick=&country_id=&zip=&int=0&order=ends&asdes=asc",
+    "http://www.egun.de/market/list_items.php?mode=qry&query=aics&plusdescr=off&wheremode=and&ewb=&status=minprice=200&maxprice=&type=0&nick=&country_id=&zip=&int=0&order=ends&asdes=asc",
+    "http://www.egun.de/market/list_items.php?mode=qry&plusdescr=off&wheremode=and&query=shr+970&quick=1",
+    "http://www.egun.de/market/list_items.php?mode=qry&query=smith+wesson+r8&plusdescr=off&wheremode=andewb=1&status=&minprice=&maxprice=&type=0&nick=&country_id=&zip=&int=0&order=ends&asdes=asc",
+    "http://www.egun.de/market/list_items.php?mode=qry&query=m305&plusdescr=off&wheremode=and&ewb=1&status=&minprice=&maxprice=&type=0&nick=&country_id=&zip=&int=0&order=ends&asdes=asc",
+    "http://www.egun.de/market/list_items.php?mode=qry&plusdescr=off&wheremode=and&maxprice=2000&query=m14+luxdef&quick=1",
+    "http://egun.de/market/list_items.php?mode=qry&query=accuracy&plusdescr=off&wheremode=and&ewb=&status=&minprice=300&maxprice=&type=0&nick=&country_id=&zip=&int=0&order=ends&asdes=asc",
+    "http://egun.de/market/list_items.php?mode=qry&plusdescr=off&wheremode=and&query=harris+1A2-L&quick=1"
+]