Архив

Архив раздела ‘Программинг’

Python: Простой пример использования twisted

Надо мне написать небольшой сервер, который будет принимать некие данные, сохранять их, а потом по определеным командам отдавать. Сейчас есть нечто такое же, только реализованное на LAMP, работает прямо скажем не очень быстро. Можно конечно сделать какое-то кеширование, memcache и т.д., но мне не хватает свободы маневра в текущей структуре, поэтому я решил таки переписать серверную часть, ибо клиентская вполне себе меня удовлетворяет.

Вообще решил сделать отдельный сервис, висящий на отличном от 80 порту и выполняющий все что мне надо. Писать решил с использование сетевого фреймворка twisted. Штука прикольная, но вот незадача, нормальной документации и примеров как то нету, того что на их сайте есть, мне явно не хватает, для того чтобы понять как и что нужно делать. Поэтому решил для начала написать простенький примерчик, который будет висеть и слушать запросы и в зависимости от url увеличивать или уменьшать внутренний счетчик.

Вот что получилось:

       1:  # To change this template, choose Tools | Templates
       2:  # and open the template in the editor.
       3:   
       4:  __author__="mrdaark"
       5:  __date__ ="$15.07.2009 21:31:01$"
       6:   
       7:  from twisted.web import server, resource
       8:  from twisted.internet import reactor
       9:  import string
      10:   
      11:  class Consult():
      12:      count=0
      13:   
      14:      def add(self,x=1):
      15:          self.__class__.count+=x
      16:   
      17:      def remove(self,x=1):
      18:          self.__class__.count-=x
      19:   
      20:  class Server(resource.Resource):
      21:      isLeaf = True
      22:   
      23:      def getChild(self, name, request):
      24:          if name == '':
      25:              return self
      26:          return Resource.getChild(self, name, request)
      27:   
      28:      def render(self, request):
      29:          urlpath=string.split(request.path[1:],'/')
      30:          path=urlpath[0]
      31:          if len(urlpath)>1:
      32:              r=int(urlpath[1])
      33:          else:
      34:              r=1
      35:   
      36:          str='<html><head></head><body>'
      37:   
      38:          c=Consult()
      39:          if (path=='add'):
      40:              c.add(r)  
      41:          elif (path=='remove'):
      42:              c.remove(r)
      43:   
      44:          str+= "x: %d<br />" % (c.count)
      45:   
      46:          str+='<a href="/add/1">add 1</a><br /><a href="/remove/1">remove 1</a></body></html>'
      47:          return str
      48:   
      49:  def main():
      50:      site = server.Site(Server())
      51:      reactor.listenTCP(8080, site)
      52:      reactor.run()
      53:   
      54:  if __name__ == "__main__":
      55:      main() 

Штука запускается, вешается на 8080 порт и принимает http запросы. Увеличивает и уменьшает внутренний счетчик в зависимости о второго параметра в урл, т.е. формат урл таков: /<функция>/<параметр>. По-умолчанию, параметр равен единице.

Вот такая штука вообщем получилась. Теперь буду усложнять этот примерчик, чтобы он сохранял и выводил список элементов, так и родится нужный мне функционал. :)

Python: RSS to Twitter

29 июня 2009 3 комментария

С недели 2 назад для одного хитрого сайта понадобилось сделать автоматический репост статей в твиттер. Решил не писать на php, а попробовать на python’е наваять. Откровенного говоря очень понравилось! Получилось очень просто и легко.

Написал наверное за пол часа, что с учетом того, что python я вообщем то не знаю совсем не плохо.

  1. #!/usr/local/bin/python
  2. # -*- coding: utf8 -*-
  3.  
  4. import urllib
  5. import libxml2
  6. import pickle
  7. import twitter
  8. import os
  9. from time import sleep
  10.  
  11. bit_api="bit.ly.api"
  12. bit_login="bit.ly.login"
  13. twit_login="name"
  14. twit_pass="pass"
  15. rss_url="http://feed"
  16.  
  17.  
  18. def shorten_url(long_url, login_user, api_key):
  19.     try:
  20.         longUrl = urllib.urlencode(dict(longUrl=long_url))
  21.         login = urllib.urlencode(dict(login=login_user))
  22.         apiKey = urllib.urlencode(dict(apiKey=api_key))
  23.  
  24.         encodedurl="http://api.bit.ly/shorten?version=2.0.1&%s&%s&%s" % (longUrl, login, apiKey)
  25.  
  26.         request = urllib.urlopen(encodedurl)
  27.         responde = request.read()
  28.         request.close()
  29.         responde_dict = eval(responde)
  30.         short_url = responde_dict["results"][long_url]["shortUrl"]
  31.         return short_url
  32.     except IOError, e:
  33.         raise "urllib error "
  34.  
  35.  
  36. if __name__ == '__main__':
  37.     doc = libxml2.parseFile(rss_url)
  38.     links=[]
  39.     for item in doc.xpathEval('//channel/item/link'):
  40.         links.append(item.content)
  41.  
  42.     titles=[]
  43.     for item in doc.xpathEval('//channel/item/title'):
  44.         titles.append(item.content)
  45.  
  46.     rss_path = os.path.join(os.path.dirname(__file__), 'rss.db')
  47.     file = open (rss_path)
  48.     old_links=pickle.load(file)
  49.     file.close()
  50.  
  51.     items=[]
  52.     f=0
  53.     for x in xrange(0,len(links)):
  54.         for old in old_links:
  55.             f=0
  56.             if (old==links[x]):
  57.                 f=1
  58.                 break
  59.         if (f==0):
  60.             items.append(x)
  61.  
  62.     api = twitter.Api(username=twit_login, password=twit_pass)
  63.  
  64.     for x in items:
  65.         title=titles[x].decode("utf8")
  66.         if len(title)<=120:
  67.             print "link %s: %s" % (x,links[x])
  68.             if x!=0:
  69.                 sleep(15)
  70.             api.PostUpdate(title+" "+shorten_url(links[x], bit_login, bit_api))
  71.  
  72.     file=open(rss_path,"w")
  73.     ser=pickle.dump(links,file)
  74.     file.close()
  75.  

Какие трудности вылезли в процессе:

1. Нужно как-то было не постить то что уже отправлено. Решилось сохранением списка ссылок в сериализованном виде в файл

2. Размер сообщение в твиттере не больше 140 символов, а надо бы в твитты еще и ссылки на статьи вставлять, притом что заголовки у статей я бы сказал не маленькие. Решилось регистрацией на bit.ly (сервис укорачивания ссылок) и собственно укорачивание. Не помню уже сам написал или с сайта их взял функцию, ибо там все так просто что даже неинтересно.

3. Невероятно, но встала проблема с кодировкой. Решилось за 5 минут гугления и пробования нескольких комбинаций. Что-то не понимал я откуда куда функции перекодируют. Вобщем вот так надо

title.decode(”utf8″)

4. Ну и задержку пришлось поставить, иначе bit.ly говорило зайдите позже :)

Дальше все это безобразие запихал в крон:

*/12 * * * * /usr/local/bin/python /usr/local/www/rss2twitter/rss2twit.py

За время пока это дело работает, уже сделан репост 1256 твиттов, так что красота.

А вообще все очень просто и легко, особенно учитывая что twitter api уже написано. хотя если бы его небыло, думаю я бы просто логинился, сохранял куки и постил обычным POST запросом.

Ну и файлик со скриптом на последок: rss2twit.tar.gz

З.Ы. с удовольствием выслушаю критику, потому как первое не Hello, world! приложение на python’е. :)

MySQL: базу знаю :)

Сегодня, не то чтобы от нечего делать, а так, прошел курс на сайте http://www.intuit.ru/ «Введение в СУБД MySQL». Прошел так, чтобы себя проверить, а то совсем самоучка, а тут хоть какое-то структурирование знаний. Честно скажу практически ничего нового для себя не вынес, пройти было на удивление легко, с первым тестом повозился (там теоретические знания по СУБД), никак он мне отлично ставить не хотел, все хорошо да хорошо. :) )

Но вообщем все остальные, направленные на практику, сдал без проблем и сразу на отлично, вообщем-то как и экзамен. Местами правда у них в вопросах неоднозначные ответы, зависящие от нераскрытых входных данных, но это фигня :)

Cертификат даже себе выписал (правда только электронный). Посмотреть можно по адресу: http://www.intuit.ru/diploma/lQ2wte8s3eAw/P00057527/. Вообщем штука прикольная, надо будет поискать еще курсов, может чего дельного для себя найду, заодно и экспиренс прокачаю :)

Изобретая велосипеды: PHP шаблонизатор

13 февраля 2009 7 комментариев

Началось все с того что мне не нравятся существующие темплейтеры типа smarty. Не прет.

Написал свой, простенький, но для повседневности хватает.

вот такой вот класс:

  1. <?php
  2. class Template {
  3.     private $template;
  4.     private $vars;
  5.     private $blocks;
  6.     private $res;
  7.     private $registry;
  8.  
  9.     function __construct($registry) {
  10.         $this->registry = $registry;
  11.     }
  12.  
  13.     private function getvars($block="")
  14.     {
  15.         $str="";
  16.         if ($block=="")
  17.         {
  18.             $str=$this->res;
  19.         }
  20.         else
  21.         {
  22.             if (isset($this->blocks[$block]))
  23.             {
  24.                 $str=$this->blocks[$block];
  25.             }
  26.         }
  27.         $m=array();
  28.         preg_match_all("/{%(.*)?%}/ismU",$str,$m,PREG_SET_ORDER);
  29.         return $this->assoc($m);
  30.     }
  31.  
  32.     private function assoc($arr)
  33.     {
  34.         $r=array();
  35.         if (count($arr)>0)
  36.         {
  37.             foreach ($arr as $v)
  38.             {
  39.                 $r[$v[1]]=$v[0];
  40.             }
  41.         }
  42.         return $r;
  43.     }
  44.  
  45.     private function getblocks()
  46.     {
  47.         $m=array();
  48.         preg_match_all("/<!--(.*)?-->.*?<!--\/\\1-->/ismU", $this->template, $m ,PREG_SET_ORDER);
  49.         $this->blocks=$this->assoc($m);
  50.     }
  51.  
  52.     private function getinclude($data)
  53.     {
  54.         $m=array();
  55.         preg_match_all("/<!--include:(.*)?-->/ismU", $this->template, $m ,PREG_SET_ORDER);
  56.         foreach ($m as $i)
  57.         {
  58.             $t=new template($this->registry);
  59.             $this->res=str_replace($i[0],$t->compile($data,site_path.$i[1]),$this->res);
  60.             unset($t);
  61.         }
  62.     }
  63.  
  64.     function getphp()
  65.     {
  66.         $m=array();
  67.         preg_match_all("/{--(.*)?--}/ismU", $this->res, $m ,PREG_SET_ORDER);
  68.         $out="";
  69.         foreach ($m as $p)
  70.         {
  71.             ob_start();
  72.             eval ("echo ".$p[1].";");
  73.             $out = ob_get_clean();
  74.             $this->res=str_replace($p[0],$out,$this->res);
  75.         }
  76.     }
  77.  
  78.     private function setvars($tpl,$vars,$d)
  79.     {
  80.         $str=$tpl;
  81.         foreach ($vars as $var => $search)
  82.         {
  83.             if (isset($d[$var]))
  84.             {
  85.                 $str=str_replace($search,$d[$var],$str);
  86.             }
  87.             else
  88.             {
  89.                 $str=str_replace($search,"",$str);
  90.             }
  91.         }
  92.         return $str;
  93.     }
  94.  
  95.     private function compileblock($block,$data)
  96.     {
  97.         $tpl=$this->blocks[$block];
  98.         $res="";
  99.         $vars = $this->getvars($block);
  100.         //print_r($vars);
  101.         foreach($data as $d)
  102.         {
  103.             $res.=$this->setvars($tpl,$vars,$d)."\n\n";
  104.         }
  105.         $this->res=str_replace($tpl,$res,$this->res);
  106.     }
  107.  
  108.     function compile($data,$tplfile)
  109.     {
  110.         $this->template=file_get_contents($tplfile);
  111.         $this->res=$this->template;
  112.         $this->getblocks();
  113.         foreach ($this->blocks as $block => &$v)
  114.         {
  115.             if (isset($data[$block]))
  116.             {
  117.                 $this->compileblock($block,$data[$block]);
  118.             }
  119.             else
  120.             {
  121.                 $this->res=str_replace($this->blocks[$block],"",$this->res);
  122.             }
  123.         }
  124.         $this->res=$this->setvars($this->res,$this->getvars(),$data);
  125.         $this->getinclude($data);
  126.         $this->getphp();
  127.  
  128.         return $this->res;
  129.     }
  130.  
  131.     function html($data,$tplfile)
  132.     {
  133.         echo $this->compile($data,site_path.$tplfile);
  134.     }
  135. }
  136. ?>

Чтобы было проще понять, вот пример шаблона:

  1. <!--include:tpl/header.tpl-->
  2. <table class="adminlist" style="width:300px;">
  3. <tr>
  4. <th class="title" width="3%"><input name="toggle" id="toggle" value="1" onclick="checkAll(1);" type="checkbox" /></th>
  5. <th class="title" width="97%">&nbsp;</th>
  6. </tr>
  7.  
  8. <!--users-->
  9. {--('1'=='{%flag%}') ? '<tr><td colspan="2">{%gname%}</td></tr>':'' --}
  10. <tr class="row0">
  11. <td><input type="checkbox" class="checkbox" name="check_{%id%}" /></td>
  12. <td><a href="/users/edit/{%id%}">{%name%}</a></td></tr>
  13. <!--/users-->
  14.  
  15. {--('write'=='{%right%}') ? '<tr><td colspan="6" style="text-align:center;"><a href="/users/create">Добавить</a></td></tr>':''--}
  16. </table>
  17.  
  18. <!--include:tpl/footer.tpl-->

Как использовать? примерно вот так:

  1. <?php
  2. $d=array();
  3. $d['users'][1]['id']=1;
  4. $d['users'][1]['name']='User 1';
  5. $d['users'][1]['game']='Group 1';
  6. $d['users'][1]['flag']=1;
  7. $d['users'][2]['id']=2;
  8. $d['users'][2]['name']='User 2';
  9. $d['users'][2]['game']='Group 1';
  10. $d['users'][2]['flag']=0;
  11. $d['users'][3]['id']=3;
  12. $d['users'][3]['name']='User 3';
  13. $d['users'][3]['game']='Group 2';
  14. $d['users'][3]['flag']=1;
  15. $d['right']='right';
  16.  
  17. $template = new Template($registry);
  18. $template->html($d,'tpl/usersform.tpl');
  19.  
  20. ?>

Поподробнее про теги шаблона:
{%name%} – переменная
<!–include:file.tpl–> – включение внешнего шаблона (количество включений ограничено только оперативной памятью :)
повторяющиеся блоки оформляются так
<!–blockname–>
{%name%}
<!–/blockname–>
где blockname – это массив значений в передаваемом параметре
условия задаются вот так:
{–(’1′==’{%flag%}’) ? ‘{%var1%}’:'{%var2%}’ –}

ссылочка на файлик класса: template.phps

Изобретая велосипеды: AJAX

2 декабря 2008 15 комментариев

короче, суть в том, что нужно мне было сделать штуку, чтобы удобно было делать запросы через ajax и не менее удобно обрабатывать ответы от них.

так вот, если раньше я таскал за собой или дикий кусок js, изменять который было сложно или некий фреймворк типа jQuery, то теперь я сделал простенький объект, который делает всю грязную работ,. а мне остается только передать параментры и функцию обработки, и поскольку я не жадный, то нате:

if (typeof jsAjax == "undefined" || !jsAjax) {
  var jsAjax = {};
}

jsAjax.ajax = typeof jsAjax.ajax != 'undefined' && jsAjax.ajax ? jsAjax.ajax : function(o) {
  var url = (typeof o === 'string') ? o : o.url;
  var callback = (typeof o.callback === 'undefined') ? function() {} : o.callback;
  var method = (typeof o.method === 'undefined') ? 'GET' : o.method;
  var type = (typeof o.type === 'undefined') ? 'text' : o.type;

  var len = (typeof o.vars === 'undefined') ? 0 : o.vars.length;
  var str="";
  for(var i = 0;i < len;i++)
  {
    if(i!='0')
    {
      str=str+'&';
    }
    str=str+o.vars[i].name+'='+escape(o.vars[i].value);
  }

  var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();

  var onCompleteRequest = function () {
    if (xhr.readyState == 4)
    {
      if (xhr.status == 200)
      {

        if (type == "text") {
          r=xhr.responseText;
        } else if (type=="json") {
           r=eval('('+xhr.responseText+')');
        } else if (type=="xml") {
          r=xhr.responseXML.documentElement;
        } else {
          r=xhr.responseText;
        }
        callback(r);
      }
    }
  };

  xhr.open(method, url, true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.setRequestHeader('Accept-Language', 'en');
  xhr.setRequestHeader('Accept-Charset', 'utf-8');
  xhr.onreadystatechange=onCompleteRequest;
  xhr.send(str);

};

Пользоваться безумно просто, чтоб было более менее понятно, несколько примеров:

jsAjax.ajax("http://example.com/");

jsAjax.ajax({url:"http://example.com/",vars:[{name:"x",value:"y"},{name:"y",value:"x"}],method:"POST",callback:function() {alert(r);}});

jsAjax.ajax({url:"http://example.com/",vars:[{name:"x",value:"y"},{name:"y",value:"x"}],method:"POST",type:"json",callback:someFunction});

теперь по поводу параметров:

можно передать просто текст, тогда он будет считаться адресом и сделает тупой GET запрос, а можно поступить умнее и заполнить структурку состоящюю из следующих полей

url – адрес который запрашиваем
vars – список параметров запроса в виде:

[{name:"someName",value:"someValue"},{name:"someOtherName",value:"someOtherValue"}]

method – метод запроса (GET или POST)
type – тип ответа передаваемый в функцию обработки (text,json,xml)
callback – функция обработки ответа (ответ в соответствии с типом как видно из кода передается одной переменной)

вообщем вызов с полным списком переменных будет выглядеть примерно так:

jsAjax.ajax({
url:"http://somedome.com/somepage.php",
vars:[{name:"someName",value:"someValue"},{name:"someOtherName",value:"someOtherValue"}],
method: "POST",
type: "text",
callback: function (answer) { alert(answer); }
});

вот вообщем и все. :)
P.S. вот ссылочка на файл исходника.

В чем сила брат? В MySQL!

Сижу пишу систему управления правами доступа пользователей к разным функциям сайта.
В итоге для вывода всех пунктов меню в соответствии с выданными правами доступа наваял такой запрос:

SELECT rlp.name,CONCAT(rlp.label,’.php’) AS ‘url’ FROM (`right_list` AS rl JOIN `group_rights` AS gr ON (rl.id=gr.right)) JOIN `right_list` AS rlp ON (rl.parent=rlp.id) WHERE gr.type=”" AND rl.label=’read’ AND gr.group=1 ORDER BY rlp.order;

Аж самому понравилось :)